· 5 years ago · Sep 17, 2020, 05:40 PM
1--[[
2 Enchat 3.0
3 Get with:
4 wget https://github.com/LDDestroier/enchat/raw/master/enchat3.lua enchat3.lua
5
6This is a stable release. You fool!
7--]]
8
9local scr_x, scr_y = term.getSize()
10CHATBOX_SAFEMODE = nil
11
12-- non-changable settings
13enchat = {
14 connectToSkynet = true,
15 version = 3.0,
16 isBeta = false,
17 port = 11000,
18 skynetPort = "enchat3-default",
19 url = "https://github.com/LDDestroier/enchat/raw/master/enchat3.lua",
20 betaurl = "https://github.com/LDDestroier/enchat/raw/beta/enchat3.lua",
21 ignoreModem = false,
22 dataDir = "/.enchat",
23 useChatbox = false,
24 disableChatboxWithRedstone = false,
25}
26
27-- changable settings
28local enchatSettings = { -- DEFAULT settings.
29 animDiv = 4, -- divisor of text animation speed (scrolling from left)
30 doAnimate = true, -- whether or not to animate text moving from left side of screen
31 reverseScroll = false, -- whether or not to make scrolling up really scroll down
32 redrawDelay = 0.1, -- delay between redrawing
33 useSetVisible = false, -- whether or not to use term.current().setVisible(), which has performance and flickering improvements
34 pageKeySpeed = 8, -- how far PageUP or PageDOWN should scroll
35 doNotif = true, -- whether or not to use oveerlay glasses for notifications, if possible
36 doKrazy = true, -- whether or not to add &k obfuscation
37 useSkynet = true, -- whether or not to use gollark's Skynet in addition to modem calls
38 extraNewline = true, -- adds an extra newline after every message since setting to true
39 acceptPictoChat = true, -- whether or not to allow tablular enchat input, which is what /picto uses
40 noRepeatNames = true, -- whether or not to display the username in two or more consecutive messages by the same user
41}
42
43-- colors for various elements
44palette = {
45 bg = colors.black,
46 txt = colors.white,
47 promptbg = colors.gray,
48 prompttxt = colors.white,
49 scrollMeter = colors.lightGray,
50 chevron = colors.black,
51 title = colors.lightGray,
52 titlebg = colors.gray,
53}
54
55-- UI adjustments, used to emulate the appearance of other chat programs
56UIconf = {
57 promptY = 1,
58 chevron = ">",
59 chatlogTop = 1,
60 title = "Enchat 3",
61 doTitle = false,
62 titleY = 1,
63 nameDecolor = false,
64 centerTitle = true,
65 prefix = "<",
66 suffix = "> "
67}
68
69-- Attempt to get some slight optimization through localizing basic functions.
70local mathmax, mathmin, mathrandom = math.max, math.min, math.random
71local termblit, termwrite = term.blit, term.write
72local termsetCursorPos, termgetCursorPos, termsetCursorBlink = term.setCursorPos, term.getCursorPos, term.setCursorBlink
73local termsetTextColor, termsetBackgroundColor = term.setTextColor, term.setBackgroundColor
74local termgetTextColor, termgetBackgroundColor = term.getTextColor, term.getBackgroundColor
75local termclear, termclearLine = term.clear, term.clearLine
76local tableinsert, tableremove, tableconcat = table.insert, table.remove, table.concat
77local textutilsserialize, textutilsunserialize = textutils.serialize, textutils.unserialize
78local stringsub, stringgsub, stringrep = string.sub, string.gsub, string.rep
79local unpack = unpack
80-- This better do something.
81
82local initcolors = {
83 bg = termgetBackgroundColor(),
84 txt = termgetTextColor()
85}
86
87local tArg = {...}
88
89local yourName = tArg[1]
90local encKey = tArg[2]
91
92local setEncKey = function(newKey)
93 encKey = newKey
94end
95
96local saveSettings = function()
97 local file = fs.open(fs.combine(enchat.dataDir, "settings"), "w")
98 file.write(
99 textutilsserialize({
100 enchatSettings = enchatSettings,
101 palette = palette,
102 UIconf = UIconf,
103 })
104 )
105 file.close()
106end
107
108local loadSettings = function()
109 local contents
110 if not fs.exists(fs.combine(enchat.dataDir, "settings")) then
111 saveSettings()
112 end
113 local file = fs.open(fs.combine(enchat.dataDir, "settings"), "r")
114 contents = file.readAll()
115 file.close()
116 local newSettings = textutilsunserialize(contents)
117 if newSettings then
118 for k,v in pairs(newSettings.enchatSettings) do
119 enchatSettings[k] = v
120 end
121 for k,v in pairs(newSettings.palette) do
122 palette[k] = v
123 end
124 for k,v in pairs(newSettings.UIconf) do
125 UIconf[k] = v
126 end
127 else
128 saveSettings()
129 end
130end
131
132local updateEnchat = function(doBeta)
133 local pPath = shell.getRunningProgram()
134 local h = http.get((doBeta or enchat.isBeta) and enchat.betaurl or enchat.url)
135 if not h then
136 return false, "Could not connect."
137 else
138 local content = h.readAll()
139 local file = fs.open(pPath, "w")
140 file.write(content)
141 file.close()
142 return true, "Updated!"
143 end
144end
145
146-- disables chat screen updating
147local pauseRendering = true
148
149-- primarily for use when using the pallete command, hoh hoh
150local colors_strnames = {
151 ["white"] = colors.white,
152 ["pearl"] = colors.white,
153 ["silver"] = colors.white,
154 ["aryan"] = colors.white,
155 ["#f0f0f0"] = colors.white,
156
157 ["orange"] = colors.orange,
158 ["carrot"] = colors.orange,
159 ["fuhrer"] = colors.orange,
160 ["pumpkin"] = colors.orange,
161 ["#f2b233"] = colors.orange,
162
163 ["magenta"] = colors.magenta,
164 ["hotpink"] = colors.magenta,
165 ["lightpurple"] = colors.magenta,
166 ["light purple"] = colors.magenta,
167 ["#e57fd8"] = colors.magenta,
168
169 ["lightblue"] = colors.lightBlue,
170 ["light blue"] = colors.lightBlue,
171 ["skyblue"] = colors.lightBlue,
172 ["#99b2f2"] = colors.lightBlue,
173
174 ["yellow"] = colors.yellow,
175 ["piss"] = colors.yellow,
176 ["pee"] = colors.yellow,
177 ["lemon"] = colors.yellow,
178 ["spongebob"] = colors.yellow,
179 ["cowardice"] = colors.yellow,
180 ["#dede6c"] = colors.yellow,
181
182 ["lime"] = colors.lime,
183 ["lightgreen"] = colors.lime,
184 ["light green"] = colors.lime,
185 ["slime"] = colors.lime,
186 ["radiation"] = colors.lime,
187 ["#7fcc19"] = colors.lime,
188
189 ["pink"] = colors.pink,
190 ["lightishred"] = colors.pink,
191 ["lightish red"] = colors.pink,
192 ["communist"] = colors.pink,
193 ["commie"] = colors.pink,
194 ["patrick"] = colors.pink,
195 ["#f2b2cc"] = colors.pink,
196
197 ["gray"] = colors.gray,
198 ["grey"] = colors.gray,
199 ["graey"] = colors.gray,
200 ["gunmetal"] = colors.gray,
201 ["#4c4c4c"] = colors.gray,
202
203 ["lightgray"] = colors.lightGray,
204 ["lightgrey"] = colors.lightGray,
205 ["light gray"] = colors.lightGray,
206 ["light grey"] = colors.lightGray,
207 ["#999999"] = colors.lightGray,
208
209 ["cyan"] = colors.cyan,
210 ["seawater"] = colors.cyan,
211 ["brine"] = colors.cyan,
212 ["#4c99b2"] = colors.cyan,
213
214 ["purple"] = colors.purple,
215 ["purble"] = colors.purple,
216 ["obsidian"] = colors.purple,
217 ["diviner"] = colors.purple,
218 ["#b266e5"] = colors.purple,
219
220 ["blue"] = colors.blue,
221 ["blu"] = colors.blue,
222 ["azure"] = colors.blue,
223 ["sapphire"] = colors.blue,
224 ["lapis"] = colors.blue,
225 ["volnutt"] = colors.blue,
226 ["blueberry"] = colors.blue,
227 ["x"] = colors.blue,
228 ["megaman"] = colors.blue,
229 ["#3366bb"] = colors.blue,
230
231 ["brown"] = colors.brown,
232 ["shit"] = colors.brown,
233 ["dirt"] = colors.brown,
234 ["mud"] = colors.brown,
235 ["bricks"] = colors.brown,
236 ["#7f664c"] = colors.brown,
237
238 ["green"] = colors.green,
239 ["grass"] = colors.green,
240 ["#57a64e"] = colors.green,
241
242 ["red"] = colors.red,
243 ["crimson"] = colors.red,
244 ["vermillion"] = colors.red,
245 ["menstration"] = colors.red,
246 ["blood"] = colors.red,
247 ["marinara"] = colors.red,
248 ["zero"] = colors.red,
249 ["protoman"] = colors.red,
250 ["communism"] = colors.red,
251 ["#cc4c4c"] = colors.red,
252
253 ["black"] = colors.black,
254 ["dark"] = colors.black,
255 ["darkness"] = colors.black,
256 ["space"] = colors.black,
257 ["coal"] = colors.black,
258 ["onyx"] = colors.black,
259 ["#191919"] = colors.black,
260}
261
262local toblit = {
263 [0] = " ",
264 [1] = "0",
265 [2] = "1",
266 [4] = "2",
267 [8] = "3",
268 [16] = "4",
269 [32] = "5",
270 [64] = "6",
271 [128] = "7",
272 [256] = "8",
273 [512] = "9",
274 [1024] = "a",
275 [2048] = "b",
276 [4096] = "c",
277 [8192] = "d",
278 [16384] = "e",
279 [32768] = "f"
280}
281local tocolors = {}
282for k,v in pairs(toblit) do
283 tocolors[v] = k
284end
285
286local codeNames = {
287 ["r"] = "reset", -- Sets either the text (&) or background (~) colors to their original color.
288 ["{"] = "stopFormatting", -- Toggles formatting text off
289 ["}"] = "startFormatting", -- Toggles formatting text on
290 ["k"] = "krazy" -- Makes the font kuh-razy!
291}
292
293-- indicates which character should turn into which random &k character
294local kraziez = {
295 ["l"] = {
296 "!",
297 "l",
298 "1",
299 "|",
300 "i",
301 "I",
302 ":",
303 ";",
304 },
305 ["m"] = {
306 "M",
307 "W",
308 "w",
309 "m",
310 "X",
311 "N",
312 "_",
313 "%",
314 "@",
315 },
316 ["all"] = {}
317}
318
319for a = 1, #kraziez["l"] do
320 kraziez[kraziez["l"][a]] = kraziez["l"]
321end
322for k,v in pairs(kraziez) do
323 for a = 1, #v do
324 kraziez[kraziez[k][a]] = v
325 end
326end
327
328-- check if using older CC version, and omit special characters if it's too old to avoid crash
329if tonumber(_CC_VERSION or 0) >= 1.76 then
330 for a = 1, 255 do
331 if (a ~= 32) and (a ~= 13) and (a ~= 10) then
332 kraziez["all"][#kraziez["all"]+1] = string.char(a)
333 end
334 end
335else
336 for a = 33, 126 do
337 kraziez["all"][#kraziez["all"]+1] = string.char(a)
338 end
339end
340
341local makeRandomString = function(length, begin, stop)
342 local output = ""
343 for a = 1, length do
344 output = output .. string.char(math.random(begin or 1, stop or 255))
345 end
346 return output
347end
348
349local personalID = makeRandomString(64, 32, 128)
350
351local explode = function(div, str, replstr, includeDiv)
352 if (div == '') then
353 return false
354 end
355 local pos, arr = 0, {}
356 for st, sp in function() return string.find(str, div, pos, false) end do
357 tableinsert(arr, string.sub(replstr or str, pos, st - 1 + (includeDiv and #div or 0)))
358 pos = sp + 1
359 end
360 tableinsert(arr, string.sub(replstr or str, pos))
361 return arr
362end
363
364local parseKrazy = function(c)
365 if kraziez[c] then
366 return kraziez[c][mathrandom(1, #kraziez[c])]
367 else
368 return kraziez.all[mathrandom(1, #kraziez.all)]
369 end
370end
371
372-- my main man, the function that turns unformatted strings into formatted strings
373local textToBlit = function(input, onlyString, initText, initBack, checkPos, useJSONformat)
374 if not input then return end
375 checkPos = checkPos or -1
376 initText, initBack = initText or toblit[term.getTextColor()], initBack or toblit[term.getBackgroundColor()]
377 tcode, bcode = "&", "~"
378 local cpos, cx = 0, 0
379 local skip, ignore, ex = nil, false, nil
380 local text, back, nex = initText, initBack, nil
381
382 local charOut, textOut, backOut = {}, {}, {}
383 local JSONoutput = {}
384
385 local krazy = false
386 local bold = false
387 local strikethrough = false
388 local underline = false
389 local italic = false
390
391 local codes = {}
392 codes["r"] = function(prev)
393 if not ignore then
394 if prev == tcode then
395 text = initText
396 bold = false
397 strikethrough = false
398 underline = false
399 italic = false
400 elseif prev == bcode then
401 if useJSONformat then
402 return 0
403 else
404 back = initBack
405 end
406 end
407 krazy = false
408 else
409 return 0
410 end
411 end
412 codes["k"] = function(prev)
413 if not ignore then
414 krazy = not krazy
415 else
416 return 0
417 end
418 end
419 codes["{"] = function(prev)
420 if not ignore then
421 ignore = true
422 else
423 return 0
424 end
425 end
426 codes["}"] = function(prev)
427 if ignore then
428 ignore = false
429 else
430 return 0
431 end
432 end
433
434 if useJSONformat then
435 codes["l"] = function(prev)
436 bold = true
437 end
438 codes["m"] = function(prev)
439 strikethrough = true
440 end
441 codes["n"] = function(prev)
442 underline = true
443 end
444 codes["o"] = function(prev)
445 italic = true
446 end
447 end
448
449 local sx, str = 0
450 input = stringgsub(input, "(\\)(%d%d?%d?)", function(cap, val)
451 if tonumber(val) < 256 then
452 cpos = cpos - #val
453 return string.char(val)
454 else
455 return cap..val
456 end
457 end)
458
459 local MCcolors = {
460 ["0"] = "white",
461 ["1"] = "gold",
462 ["2"] = "light_purple",
463 ["3"] = "aqua",
464 ["4"] = "yellow",
465 ["5"] = "green",
466 ["6"] = "light_purple",
467 ["7"] = "dark_gray",
468 ["8"] = "gray",
469 ["9"] = "dark_aqua",
470 ["a"] = "dark_purple",
471 ["b"] = "dark_blue",
472 ["c"] = "gold",
473 ["d"] = "dark_green",
474 ["e"] = "red",
475 ["f"] = "black",
476 }
477
478 for cx = 1, #input do
479 str = stringsub(input,cx,cx)
480 if skip then
481 if tocolors[str] and not ignore then
482 if skip == tcode then
483 text = str == " " and initText or str
484 if sx < checkPos then
485 cpos = cpos - 2
486 end
487 elseif skip == bcode then
488 back = str == " " and initBack or str
489 if sx < checkPos then
490 cpos = cpos - 2
491 end
492 end
493 elseif codes[str] and not (ignore and str == "{") then
494 ex = codes[str](skip) or 0
495 sx = sx + ex
496 if sx < checkPos then
497 cpos = cpos - ex - 2
498 end
499 else
500 sx = sx + 1
501 if useJSONformat then
502 JSONoutput[sx] = {
503 text = (skip..str),
504 color = onlyString and "f" or MCcolors[text],
505 bold = (not onlyString) and bold,
506 italic = (not onlyString) and italic,
507 underline = (not onlyString) and underline,
508 obfuscated = (not onlyString) and krazy,
509 strikethrough = (not onlyString) and strikethrough
510 }
511 else
512 charOut[sx] = krazy and parseKrazy(prev..str) or (skip..str)
513 textOut[sx] = stringrep(text,2)
514 backOut[sx] = stringrep(back,2)
515 end
516 end
517 skip = nil
518 else
519 if (str == tcode or str == bcode) and (codes[stringsub(input, 1+cx, 1+cx)] or tocolors[stringsub(input,1+cx,1+cx)]) then
520 skip = str
521 else
522 sx = sx + 1
523 if useJSONformat then
524 JSONoutput[sx] = {
525 text = str,
526 color = onlyString and "f" or MCcolors[text],
527 bold = (not onlyString) and bold,
528 italic = (not onlyString) and italic,
529 underline = (not onlyString) and underline,
530 obfuscated = (not onlyString) and krazy,
531 strikethrough = (not onlyString) and strikethrough
532 }
533 else
534 charOut[sx] = krazy and parseKrazy(str) or str
535 textOut[sx] = text
536 backOut[sx] = back
537 end
538 end
539 end
540 end
541 if useJSONformat then
542 return textutils.serializeJSON(JSONoutput)
543 else
544 if onlyString then
545 return tableconcat(charOut), (checkPos > -1) and cpos or nil
546 else
547-- return {tableconcat(charOut), tableconcat(textOut):gsub(" ", initText), tableconcat(backOut):gsub(" ", initBack)}, (checkPos > -1) and cpos or nil
548 return {tableconcat(charOut), tableconcat(textOut), tableconcat(backOut)}, (checkPos > -1) and cpos or nil
549 end
550 end
551end
552_G.textToBlit = textToBlit
553
554-- convoluted read function that renders color codes as they are written.
555-- struggles with \123 codes, but hey, fuck you
556local colorRead = function(maxLength, _history)
557 local output = ""
558 local history, _history = {}, _history or {}
559 for a = 1, #_history do
560 history[a] = _history[a]
561 end
562 history[#history+1] = ""
563 local hPos = #history
564 local cx, cy = termgetCursorPos()
565 local x, xscroll = 1, 1
566 local ctrlDown = false
567 termsetCursorBlink(true)
568 local evt, key, bout, xmod, timtam
569 while true do
570 termsetCursorPos(cx, cy)
571 bout, xmod = textToBlit(output, false, nil, nil, x)
572 for a = 1, #bout do
573 bout[a] = stringsub(bout[a], xscroll, xscroll + scr_x - cx)
574 end
575 termblit(unpack(bout))
576 termwrite((" "):rep(scr_x - cx))
577 termsetCursorPos(cx + x + xmod - xscroll, cy)
578 evt = {os.pullEvent()}
579 if evt[1] == "char" or evt[1] == "paste" then
580 output = (output:sub(1, x-1)..evt[2]..output:sub(x)):sub(1, maxLength or -1)
581 x = mathmin(x + #evt[2], #output+1)
582 elseif evt[1] == "key" then
583 key = evt[2]
584 if key == keys.leftCtrl then
585 ctrlDown = true
586 elseif key == keys.left then
587 x = mathmax(x - 1, 1)
588 elseif key == keys.right then
589 x = mathmin(x + 1, #output+1)
590 elseif key == keys.backspace then
591 if x > 1 then
592 repeat
593 output = output:sub(1,x-2)..output:sub(x)
594 x = x - 1
595 until output:sub(x-1,x-1) == " " or (not ctrlDown) or (x == 1)
596 end
597 elseif key == keys.delete then
598 if x < #output+1 then
599 repeat
600 output = output:sub(1,x-1)..output:sub(x+1)
601 until output:sub(x,x) == " " or (not ctrlDown) or (x == #output+1)
602 end
603 elseif key == keys.enter then
604 termsetCursorBlink(false)
605 return output
606 elseif key == keys.home then
607 x = 1
608 elseif key == keys["end"] then
609 x = #output+1
610 elseif key == keys.up then
611 if history[hPos-1] then
612 hPos = hPos - 1
613 output = history[hPos]
614 x = #output+1
615 end
616 elseif key == keys.down then
617 if history[hPos+1] then
618 hPos = hPos + 1
619 output = history[hPos]
620 x = #output+1
621 end
622 end
623 elseif evt[1] == "key_up" then
624 if evt[2] == keys.leftCtrl then
625 ctrlDown = false
626 end
627 end
628 if hPos > 1 then
629 history[hPos] = output
630 end
631 if x+cx-xscroll+xmod > scr_x then
632 xscroll = x-(scr_x-cx)+xmod
633 elseif x-xscroll+xmod < 0 then
634 repeat
635 xscroll = xscroll - 1
636 until x-xscroll-xmod >= 0
637 end
638 xscroll = math.max(1, xscroll)
639 end
640end
641_G.colorRead = colorRead
642
643local checkValidName = function(_nayme)
644 local nayme = textToBlit(_nayme,true)
645 if type(nayme) ~= "string" then
646 return false
647 else
648 return (#nayme >= 2 and #nayme <= 32 and nayme:gsub(" ","") ~= "")
649 end
650end
651
652if tArg[1] == "update" then
653 local res, message = updateEnchat(tArg[2] == "beta")
654 return print(message)
655end
656
657local prettyClearScreen = function(start, stop)
658 termsetTextColor(colors.lightGray)
659 termsetBackgroundColor(colors.gray)
660 if _VERSION then
661 for y = start or 1, stop or scr_y do
662 termsetCursorPos(1,y)
663 if y == (start or 1) then
664 termwrite(("\135"):rep(scr_x))
665 elseif y == (stop or scr_y) then
666 termsetTextColor(colors.gray)
667 termsetBackgroundColor(colors.lightGray)
668 termwrite(("\135"):rep(scr_x))
669 else
670 termclearLine()
671 end
672 end
673 else
674 termclear()
675 end
676end
677
678local cwrite = function(text, y)
679 local cx, cy = termgetCursorPos()
680 termsetCursorPos((scr_x/2) - math.ceil(#text/2), y or cy)
681 return write(text)
682end
683
684local prettyCenterWrite = function(text, y)
685 local words = explode(" ", text, nil, true)
686 local buff = ""
687 local lines = 0
688 for w = 1, #words do
689 if #buff + #words[w] > scr_x then
690 cwrite(buff, y + lines)
691 buff = ""
692 lines = lines + 1
693 end
694 buff = buff..words[w]
695 end
696 cwrite(buff, y + lines)
697 return lines
698end
699
700local prettyPrompt = function(prompt, y, replchar, doColor)
701 local cy, cx = termgetCursorPos()
702 termsetBackgroundColor(colors.gray)
703 termsetTextColor(colors.white)
704 local yadj = 1 + prettyCenterWrite(prompt, y or cy)
705 termsetCursorPos(1, y + yadj)
706 termsetBackgroundColor(colors.lightGray)
707 termclearLine()
708 local output
709 if doColor then
710 output = colorRead()
711 else
712 output = read(replchar)
713 end
714 return output
715end
716
717local fwrite = function(text)
718 local b = textToBlit(text)
719 return termblit(unpack(b))
720end
721
722local cfwrite = function(text, y)
723 local cx, cy = termgetCursorPos()
724 termsetCursorPos((scr_x/2) - math.ceil(#textToBlit(text,true)/2), y or cy)
725 return fwrite(text)
726end
727
728-- execution start!
729
730if not checkValidName(yourName) then -- not so fast, evildoers
731 yourName = nil
732end
733
734local currentY = 2
735
736if not (yourName and encKey) then
737 prettyClearScreen()
738end
739
740if not yourName then
741 cfwrite("&8~7Text = &, Background = ~", scr_y-3)
742 cfwrite("&8~7&{Krazy = &k, Reset = &r", scr_y-2)
743 cfwrite("&7~00~11~22~33~44~55~66&8~77&7~88~99~aa~bb~cc~dd~ee~ff", scr_y-1)
744 yourName = prettyPrompt("Enter your name.", currentY, nil, true)
745 if not checkValidName(yourName) then
746 while true do
747 yourName = prettyPrompt("That name isn't valid. Enter another.", currentY, nil, true)
748 if checkValidName(yourName) then
749 break
750 end
751 end
752 end
753 currentY = currentY + 3
754end
755
756if not encKey then
757 setEncKey(prettyPrompt("Enter an encryption key.", currentY, "*"))
758 currentY = currentY + 3
759end
760
761-- prevents terminating. it is reversed upon exit.
762local oldePullEvent = os.pullEvent
763os.pullEvent = os.pullEventRaw
764
765local bottomMessage = function(text)
766 termsetCursorPos(1,scr_y)
767 termsetTextColor(colors.gray)
768 termclearLine()
769 termwrite(text)
770end
771
772loadSettings()
773saveSettings()
774
775termsetBackgroundColor(colors.black)
776termclear()
777
778local getAPI = function(apiname, apipath, apiurl, doDoFile, doScroll)
779 apipath = fs.combine(fs.combine(enchat.dataDir,"api"), apipath)
780 if (not fs.exists(apipath)) then
781 if doScroll then term.scroll(1) end
782 bottomMessage(apiname .. " API not found! Downloading...")
783 local prog = http.get(apiurl)
784 if not prog then
785 if doScroll then term.scroll(1) end
786 bottomMessage("Failed to download " .. apiname .. " API. Abort.")
787 termsetCursorPos(1,1)
788 return
789 end
790 local file = fs.open(apipath,"w")
791 file.write(prog.readAll())
792 file.close()
793 end
794 if doDoFile then
795 return dofile(apipath)
796 else
797 os.loadAPI(apipath)
798 end
799 if not _ENV[fs.getName(apipath)] then
800 if doScroll then term.scroll(1) end
801 bottomMessage("Failed to load " .. apiname .. " API. Abort.")
802 termsetCursorPos(1,1)
803 return
804 else
805 return _ENV[fs.getName(apipath)]
806 end
807end
808
809local skynet, aes, bigfont
810-- _G.skynet_CBOR_path = fs.combine(enchat.dataDir,"/api/cbor")
811aes = getAPI("AES", "aes", "http://pastebin.com/raw/9E5UHiqv", false, false)
812if enchat.connectToSkynet and http.websocket then
813 skynet = getAPI("Skynet", "skynet", "https://raw.githubusercontent.com/LDDestroier/CC/master/API/skynet.lua", true, true)
814end
815bigfont = getAPI("BigFont", "bigfont", "https://pastebin.com/raw/3LfWxRWh", false, true)
816
817if encKey and skynet and enchat.connectToSkynet then
818 bottomMessage("Connecting to Skynet...")
819 local success = parallel.waitForAny(
820 function()
821 skynet.open(enchat.skynetPort)
822 end,
823 function()
824 sleep(3)
825 end
826 )
827 if success == 2 then
828 term.scroll(1)
829 bottomMessage("Failed to connect to skynet.")
830 skynet = nil
831 sleep(0.5)
832 end
833end
834
835local log = {} -- Records all sorts of data on text.
836local renderlog = {} -- Only records straight terminal output. Generated from 'log'
837local IDlog = {} -- Really only used with skynet, will prevent duplicate messages.
838
839local scroll = 0
840local maxScroll = 0
841
842local getModem = function()
843 if enchat.ignoreModem then
844 return nil
845 else
846 local modems = {peripheral.find("modem")}
847 return modems[1]
848 end
849end
850
851local getChatbox = function()
852 if enchat.useChatbox then
853 if commands then -- oh baby, a command computer, now we're talkin'
854 -- mind you, you still need a chatbox to get chat input...
855 return {
856 say = function(text)
857 commands.tellraw("@a", textToBlit(text, false, "0", "f", nil, true))
858 end,
859 tell = function(player, text)
860 commands.tellraw(player, textToBlit(text, false, "0", "f", nil, true))
861 end
862 }
863 else
864 local cb = chatbox or peripheral.find("chat_box")
865 if cb then
866 if cb.setName then -- Computronics
867 cb.setName(yourName)
868 return {
869 say = cb.say,
870 tell = cb.say -- why is there no tell command???
871 }
872 else -- whatever whackjob mod SwitchCraft uses I forget
873 return {
874 say = function(text, block)
875 if CHATBOX_SAFEMODE then
876-- if CHATBOX_SAFEMODE ~= block then
877 cb.tell(CHATBOX_SAFEMODE, text)
878-- end
879 else
880 local players = cb.getPlayerList()
881 for i = 1, #players do
882 if players[i] ~= block then
883 cb.tell(players[i], text)
884 end
885 end
886 end
887 end,
888 tell = cb.tell
889 }
890 end
891 else
892 return nil
893 end
894 end
895 else
896 return nil
897 end
898end
899
900local modem = getModem()
901local chatbox = getChatbox()
902
903if (not modem) and (not enchat.ignoreModem) then
904 if ccemux and (not enchat.ignoreModem) then
905 ccemux.attach("top", "wireless_modem")
906 modem = getModem()
907 elseif not skynet then
908 error("You should get a modem.")
909 end
910end
911
912if modem then modem.open(enchat.port) end
913
914local modemTransmit = function(freq, repfreq, message)
915 if modem then
916 modem.transmit(freq, repfreq, message)
917 end
918end
919
920local encrite = function(input) -- standardized encryption function
921 if not input then return input end
922 return aes.encrypt(encKey, textutilsserialize(input))
923end
924
925local decrite = function(input) -- redundant comments cause tuberculosis
926 if not input then return input end
927 return textutilsunserialize(aes.decrypt(encKey, input) or "")
928end
929
930local dab = function(func, ...) -- "do and back", not...never mind
931 local x, y = termgetCursorPos()
932 local b, t = termgetBackgroundColor(), termgetTextColor()
933 local output = {func(...)}
934 termsetCursorPos(x,y)
935 termsetTextColor(t)
936 termsetBackgroundColor(b)
937 return unpack(output)
938end
939
940local splitStr = function(str, maxLength)
941 local output = {}
942 for l = 1, #str, maxLength do
943 output[#output+1] = str:sub(l,l+maxLength+-1)
944 end
945 return output
946end
947
948local splitStrTbl = function(tbl, maxLength)
949 local output, tline = {}
950 for w = 1, #tbl do
951 tline = splitStr(tbl[w], maxLength)
952 for t = 1, #tline do
953 output[#output+1] = tline[t]
954 end
955 end
956 return output
957end
958
959-- same as term.blit, but wraps by-word.
960local blitWrap = function(char, text, back, noWrite) -- where ALL of the onscreen wrapping is done
961 local cWords = splitStrTbl(explode(" ",char,nil, true), scr_x)
962 local tWords = splitStrTbl(explode(" ",char,text,true), scr_x)
963 local bWords = splitStrTbl(explode(" ",char,back,true), scr_x)
964
965 local ox,oy = termgetCursorPos()
966 local cx,cy,ty = ox,oy,1
967 local output = {}
968 local length = 0
969 local maxLength = 0
970 for a = 1, #cWords do
971 length = length + #cWords[a]
972 maxLength = mathmax(maxLength, length)
973 if ((cx + #cWords[a]) > scr_x) then
974 cx = 1
975 length = 0
976 if (cy == scr_y) then
977 term.scroll(1)
978 end
979 cy = mathmin(cy+1, scr_y)
980 ty = ty + 1
981 end
982 if not noWrite then
983 termsetCursorPos(cx,cy)
984 termblit(cWords[a],tWords[a],bWords[a])
985 end
986 cx = cx + #cWords[a]
987 output[ty] = output[ty] or {"","",""}
988 output[ty][1] = output[ty][1]..cWords[a]
989 output[ty][2] = output[ty][2]..tWords[a]
990 output[ty][3] = output[ty][3]..bWords[a]
991 end
992 return output, maxLength
993end
994
995-- simple picture drawing function, for /picto
996local pictochat = function(xsize, ysize)
997 local output = {{},{},{}}
998 local maxWidth, minMargin = 0, math.huge
999 for y = 1, ysize do
1000 output[1][y] = {}
1001 output[2][y] = {}
1002 output[3][y] = {}
1003 for x = 1, xsize do
1004 output[1][y][x] = " "
1005 output[2][y][x] = " "
1006 output[3][y][x] = " "
1007 end
1008 end
1009
1010 termsetBackgroundColor(colors.gray)
1011 termsetTextColor(colors.black)
1012 for y = 1, scr_y do
1013 termsetCursorPos(1, y)
1014 termwrite(("/"):rep(scr_x))
1015 end
1016 cwrite(" [ENTER] to finish. ", scr_y)
1017 cwrite("Push a key to change char.", scr_y-1)
1018
1019 local cx, cy = math.floor((scr_x/2)-(xsize/2)), math.floor((scr_y/2)-(ysize/2))
1020
1021 local allCols = "0123456789abcdef"
1022 local tPos, bPos = 16, 1
1023 local char, text, back = " ", allCols:sub(tPos,tPos), allCols:sub(bPos,bPos)
1024
1025 local render = function()
1026 termsetTextColor(colors.white)
1027 termsetBackgroundColor(colors.black)
1028 local mx, my
1029 for y = 1, ysize do
1030 for x = 1, xsize do
1031 mx, my = x+cx+-1, y+cy+-1
1032 termsetCursorPos(mx,my)
1033 termblit(output[1][y][x], output[2][y][x], output[3][y][x])
1034 end
1035 end
1036 termsetCursorPos((scr_x/2)-5,ysize+cy+1)
1037 termwrite("Char = '")
1038 termblit(char, text, back)
1039 termwrite("'")
1040 end
1041 local evt, butt, mx, my
1042 local isShiftDown = false
1043
1044 render()
1045
1046 while true do
1047 evt = {os.pullEvent()}
1048 if evt[1] == "mouse_click" or evt[1] == "mouse_drag" then
1049 butt, mx, my = evt[2], evt[3]-cx+1, evt[4]-cy+1
1050 if mx >= 1 and mx <= xsize and my >= 1 and my <= ysize then
1051 if butt == 1 then
1052 output[1][my][mx] = char
1053 output[2][my][mx] = text
1054 output[3][my][mx] = back
1055 elseif butt == 2 then
1056 output[1][my][mx] = " "
1057 output[2][my][mx] = " "
1058 output[3][my][mx] = " "
1059 end
1060 render()
1061 end
1062 elseif evt[1] == "mouse_scroll" then
1063 local oldTpos, oldBpos = tPos, bPos
1064 if isShiftDown then
1065 tPos = mathmax(1, mathmin(16, tPos + evt[2]))
1066 else
1067 bPos = mathmax(1, mathmin(16, bPos + evt[2]))
1068 end
1069 text, back = stringsub(allCols,tPos,tPos), stringsub(allCols,bPos,bPos)
1070 if oldTpos ~= tPos or oldBpos ~= bPos then
1071 render()
1072 end
1073 elseif evt[1] == "key" then
1074 if evt[2] == keys.enter then
1075 for y = 1, ysize do
1076 output[1][y] = table.concat(output[1][y])
1077 output[2][y] = table.concat(output[2][y])
1078 output[3][y] = table.concat(output[3][y])
1079 maxWidth = math.max(maxWidth, #stringgsub(output[3][y], " +$", ""))
1080 minMargin = math.min(minMargin, output[3][y]:find("[^ ]") or math.huge)
1081 end
1082 --error(minMargin)
1083 local croppedOutput = {}
1084 local touched = false
1085 local crY = 0
1086 for a = 1, ysize do
1087 if output[1][1] == (" "):rep(xsize) and output[3][1] == (" "):rep(xsize) then
1088 tableremove(output[1],1)
1089 tableremove(output[2],1)
1090 tableremove(output[3],1)
1091 else
1092 for y = #output[1], 1, -1 do
1093 if output[1][y] == (" "):rep(xsize) and output[3][y] == (" "):rep(xsize) then
1094 tableremove(output[1],y)
1095 tableremove(output[2],y)
1096 tableremove(output[3],y)
1097 else
1098 break
1099 end
1100 end
1101 break
1102 end
1103 end
1104 for y = 1, #output[1] do
1105 output[1][y] = output[1][y]:sub(minMargin, maxWidth)
1106 output[2][y] = output[2][y]:sub(minMargin, maxWidth)
1107 output[3][y] = output[3][y]:sub(minMargin, maxWidth)
1108 end
1109 return output
1110 elseif evt[2] == keys.leftShift then
1111 isShiftDown = true
1112 elseif evt[2] == keys.left or evt[2] == keys.right then
1113 local oldTpos, oldBpos = tPos, bPos
1114 if isShiftDown then
1115 tPos = mathmax(1, mathmin(16, tPos + (evt[2] == keys.right and 1 or -1)))
1116 else
1117 bPos = mathmax(1, mathmin(16, bPos + (evt[2] == keys.right and 1 or -1)))
1118 end
1119 text, back = allCols:sub(tPos,tPos), allCols:sub(bPos,bPos)
1120 if oldTpos ~= tPos or oldBpos ~= bPos then
1121 render()
1122 end
1123 end
1124 elseif evt[1] == "key_up" then
1125 if evt[2] == keys.leftShift then
1126 isShiftDown = false
1127 end
1128 elseif evt[1] == "char" then
1129 if char ~= evt[2] then
1130 char = evt[2]
1131 render()
1132 end
1133 end
1134 end
1135end
1136
1137-- notifications will only appear if you have plethora's neural connector and overlay glasses on your person
1138
1139local notif = {}
1140notif.alpha = 248
1141notif.height = 10
1142notif.width = 6
1143notif.time = 40
1144notif.wrapX = 350
1145notif.maxNotifs = 15
1146local nList = {}
1147local colorTranslate = {
1148 [" "] = {240, 240, 240},
1149 ["0"] = {240, 240, 240},
1150 ["1"] = {242, 178, 51 },
1151 ["2"] = {229, 127, 216},
1152 ["3"] = {153, 178, 242},
1153 ["4"] = {222, 222, 108},
1154 ["5"] = {127, 204, 25 },
1155 ["6"] = {242, 178, 204},
1156 ["7"] = {76, 76, 76 },
1157 ["8"] = {153, 153, 153},
1158 ["9"] = {76, 153, 178},
1159 ["a"] = {178, 102, 229},
1160 ["b"] = {51, 102, 204},
1161 ["c"] = {127, 102, 76 },
1162 ["d"] = {87, 166, 78 },
1163 ["e"] = {204, 76, 76 },
1164 ["f"] = {25, 25, 25 }
1165}
1166local interface, canvas = peripheral.find("neuralInterface")
1167if interface then
1168 if interface.canvas then
1169 canvas = interface.canvas()
1170 notif.newNotification = function(char, text, back, time)
1171 if #nList > notif.maxNotifs then
1172 tableremove(nList, 1)
1173 end
1174 nList[#nList+1] = {char,text,back,time,1} -- the last one is the alpha multiplier
1175 end
1176 notif.displayNotifications = function(doCountDown)
1177 local adjList = {
1178 ["i"] = -4,
1179 ["l"] = -3,
1180 ["I"] = -1,
1181 ["t"] = -2,
1182 ["k"] = -1,
1183 ["!"] = -4,
1184 ["|"] = -4,
1185 ["."] = -4,
1186 [","] = -4,
1187 [":"] = -4,
1188 [";"] = -4,
1189 ["f"] = -1,
1190 ["'"] = -3,
1191 ["\""] = -1,
1192 ["<"] = -1,
1193 [">"] = -1,
1194 }
1195 local drawEdgeLine = function(y,alpha)
1196 local l = canvas.addRectangle(notif.wrapX, 1+(y-1)*notif.height, 1, notif.height)
1197 l.setColor(unpack(colorTranslate["0"]))
1198 l.setAlpha(alpha / 2)
1199 end
1200 local getWordWidth = function(str)
1201 local output = 0
1202 for a = 1, #str do
1203 output = output + notif.width + (adjList[stringsub(str,a,a)] or 0)
1204 end
1205 return output
1206 end
1207 canvas.clear()
1208 local xadj, charadj, wordadj, t, r
1209 local x, y, words, txtwords, bgwords = 0, 0
1210 for n = 1, mathmin(#nList, notif.maxNotifs) do
1211 xadj, charadj = 0, 0
1212 y = y + 1
1213 x = 0
1214 words = explode(" ",nList[n][1],nil,true)
1215 txtwords = explode(" ",nList[n][1],nList[n][2],true)
1216 bgwords = explode(" ",nList[n][1],nList[n][3],true)
1217 local char, text, back
1218 local currentX = 0
1219 for w = 1, #words do
1220 char = words[w]
1221 text = txtwords[w]
1222 back = bgwords[w]
1223 if currentX + getWordWidth(char) > notif.wrapX then
1224 y = y + 1
1225 x = 2
1226 xadj = 0
1227 currentX = x * notif.width
1228 end
1229 for cx = 1, #char do
1230 x = x + 1
1231 charadj = (adjList[stringsub(char,cx,cx)] or 0)
1232 r = canvas.addRectangle(xadj+1+(x-1)*notif.width, 1+(y-1)*notif.height, charadj+notif.width, notif.height)
1233 if stringsub(back,cx,cx) ~= " " then
1234 r.setAlpha(notif.alpha * nList[n][5])
1235 r.setColor(unpack(colorTranslate[stringsub(back,cx,cx)]))
1236 else
1237 r.setAlpha(100 * nList[n][5])
1238 r.setColor(unpack(colorTranslate["7"]))
1239 end
1240 drawEdgeLine(y,notif.alpha * nList[n][5])
1241 t = canvas.addText({xadj+1+(x-1)*notif.width,2+(y-1)*notif.height}, stringsub(char,cx,cx))
1242 t.setAlpha(notif.alpha * nList[n][5])
1243 t.setColor(unpack(colorTranslate[stringsub(text,cx,cx)]))
1244 xadj = xadj + charadj
1245 currentX = currentX + charadj+notif.width
1246 end
1247 end
1248 end
1249 for n = mathmin(#nList, notif.maxNotifs), 1, -1 do
1250 if doCountDown then
1251 if nList[n][4] > 1 then
1252 nList[n][4] = nList[n][4] - 1
1253 else
1254 if nList[n][5] > 0 then
1255 while true do
1256 nList[n][5] = mathmax(nList[n][5] - 0.2, 0)
1257 notif.displayNotifications(false)
1258 if nList[n][5] == 0 then break else sleep(0.05) end
1259 end
1260 end
1261 tableremove(nList,n)
1262 end
1263 end
1264 end
1265 end
1266 end
1267end
1268
1269local darkerCols = {
1270 ["0"] = "8",
1271 ["1"] = "c",
1272 ["2"] = "a",
1273 ["3"] = "b",
1274 ["4"] = "1",
1275 ["5"] = "d",
1276 ["6"] = "2",
1277 ["7"] = "f",
1278 ["8"] = "7",
1279 ["9"] = "b",
1280 ["a"] = "7",
1281 ["b"] = "7",
1282 ["c"] = "f",
1283 ["d"] = "7",
1284 ["e"] = "7",
1285 ["f"] = "f"
1286}
1287
1288-- used for regular chat. they can be disabled if you hate fun
1289local animations = {
1290 slideFromLeft = function(char, text, back, frame, maxFrame, length)
1291 return {
1292 stringsub(char, (length or #char) - ((frame/maxFrame)*(length or #char))),
1293 stringsub(text, (length or #text) - ((frame/maxFrame)*(length or #text))),
1294 stringsub(back, (length or #back) - ((frame/maxFrame)*(length or #back)))
1295 }
1296 end,
1297 fadeIn = function(char, text, back, frame, maxFrame, length)
1298 -- a good example:
1299 -- &1what &2in &3the &4world &5are &6you &7doing &8in &9my &aswamp
1300 for i = 1, 3 - math.ceil(frame/maxFrame * 3) do
1301 text = stringgsub(text, ".", darkerCols)
1302 end
1303 return {
1304 char,
1305 text,
1306 back
1307 }
1308 end,
1309 flash = function(char, text, back, frame, maxFrame, length)
1310 local t = palette.txt
1311 if frame ~= maxFrame then
1312 t = (frame % 2 == 0) and t or palette.bg
1313 end
1314 return {
1315 char,
1316 toblit[t]:rep(#text),
1317 (frame % 2 == 0) and back or (" "):rep(#back)
1318 }
1319 end,
1320 none = function(char, text, back, frame, maxFrame, length)
1321 return {
1322 char,
1323 text,
1324 back
1325 }
1326 end
1327}
1328
1329local inAnimate = function(animType, buff, frame, maxFrame, length)
1330 local char, text, back = buff[1], buff[2], buff[3]
1331 if enchatSettings.doAnimate and (frame >= 0) and (maxFrame > 0) then
1332 return animations[animType or "slideFromleft"](char, text, back, frame, maxFrame, length)
1333 else
1334 return {char,text,back}
1335 end
1336end
1337
1338local genRenderLog = function()
1339 local buff, prebuff, maxLength, lastUser
1340 local scrollToBottom = scroll == maxScroll
1341 renderlog = {}
1342 local dcName, dcMessage
1343 for a = 1, #log do
1344 if not ((lastUser == log[a].personalID and log[a].personalID) and log[a].name == "" and log[a].message == " ") then
1345 termsetCursorPos(1,1)
1346 if UIconf.nameDecolor then
1347 if lastUser == log[a].personalID and log[a].personalID then
1348 dcName = ""
1349 else
1350 dcName = textToBlit(table.concat({log[a].prefix,log[a].name,log[a].suffix}), true, toblit[palette.txt], toblit[palette.bg])
1351 end
1352 dcMessage = textToBlit(log[a].message, false, toblit[palette.txt], toblit[palette.bg])
1353 prebuff = {
1354 dcName..dcMessage[1],
1355 toblit[palette.chevron]:rep(#dcName)..dcMessage[2],
1356 toblit[palette.bg]:rep(#dcName)..dcMessage[3]
1357 }
1358 else
1359 if lastUser == log[a].personalID and log[a].personalID then
1360 prebuff = textToBlit(" " .. log[a].message, false, toblit[palette.txt], toblit[palette.bg])
1361 else
1362 prebuff = textToBlit(table.concat({
1363 log[a].prefix,
1364 "&}&r~r",
1365 log[a].name,
1366 "&}&r~r",
1367 log[a].suffix,
1368 "&}&r~r",
1369 log[a].message
1370 }),
1371 false, toblit[palette.txt], toblit[palette.bg])
1372 end
1373 end
1374 if log[a].message ~= " " and enchatSettings.noRepeatNames then
1375 lastUser = log[a].personalID
1376 end
1377 if (log[a].frame == 0) and (canvas and enchatSettings.doNotif) then
1378 if not (log[a].name == "" and log[a].message == " ") then
1379 notif.newNotification(prebuff[1], prebuff[2], prebuff[3], notif.time * 4)
1380 end
1381 end
1382 if log[a].maxFrame == true then
1383 log[a].maxFrame = math.floor(mathmin(#prebuff[1], scr_x) / enchatSettings.animDiv)
1384 end
1385 if log[a].ignoreWrap then
1386 buff, maxLength = {prebuff}, mathmin(#prebuff[1], scr_x)
1387 else
1388 buff, maxLength = blitWrap(prebuff[1], prebuff[2], prebuff[3], true)
1389 end
1390 -- repeat every line in multiline entries
1391 for l = 1, #buff do
1392 -- holy shit, two animations, lookit mr. roxas over here
1393 if log[a].animType then
1394 renderlog[#renderlog + 1] = inAnimate(log[a].animType, buff[l], log[a].frame, log[a].maxFrame, maxLength)
1395 else
1396 renderlog[#renderlog + 1] = inAnimate("fadeIn", inAnimate("slideFromLeft", buff[l], log[a].frame, log[a].maxFrame, maxLength), log[a].frame, log[a].maxFrame, maxLength)
1397 end
1398 end
1399 if (log[a].frame < log[a].maxFrame) and log[a].frame >= 0 then
1400 log[a].frame = log[a].frame + 1
1401 else
1402 log[a].frame = -1
1403 end
1404 end
1405 end
1406 maxScroll = mathmax(0, #renderlog - (scr_y - 2))
1407 if scrollToBottom then
1408 scroll = maxScroll
1409 end
1410end
1411
1412-- there is probably a much better way of doing this, but I don't care at the moment
1413local tsv = function(visible)
1414 if term.current().setVisible and enchatSettings.useSetVisible then
1415 return term.current().setVisible(visible)
1416 end
1417end
1418
1419local renderChat = function(doScrollBackUp)
1420 tsv(false)
1421 termsetCursorBlink(false)
1422 genRenderLog(log)
1423 local ry
1424 termsetBackgroundColor(palette.bg)
1425 for y = UIconf.chatlogTop, (scr_y-UIconf.promptY) - 1 do
1426 ry = (y + scroll - (UIconf.chatlogTop - 1))
1427 termsetCursorPos(1,y)
1428 termclearLine()
1429 if renderlog[ry] then
1430 termblit(unpack(renderlog[ry]))
1431 end
1432 end
1433 if UIconf.promptY ~= 0 then
1434 termsetCursorPos(1,scr_y)
1435 termsetTextColor(palette.scrollMeter)
1436 termclearLine()
1437 termwrite(scroll.." / "..maxScroll.." ")
1438 end
1439
1440 local _title = UIconf.title:gsub("YOURNAME", yourName.."&}&r~r"):gsub("ENCKEY", encKey.."&}&r~r"):gsub("PORT", tostring(enchat.port))
1441 if UIconf.doTitle then
1442 termsetTextColor(palette.title)
1443 term.setBackgroundColor(palette.titlebg)
1444 if UIconf.nameDecolor then
1445 if UIconf.centerTitle then
1446 cwrite((" "):rep(scr_x)..textToBlit(_title, true)..(" "):rep(scr_x), UIconf.titleY or 1)
1447 else
1448 termsetCursorPos(1, UIconf.titleY or 1)
1449 termwrite(textToBlit(_title, true)..(" "):rep(scr_x))
1450 end
1451 else
1452 local blTitle = textToBlit(_title)
1453 termsetCursorPos(UIconf.centerTitle and ((scr_x/2) - math.ceil(#blTitle[1]/2)) or 1, UIconf.titleY or 1)
1454 termclearLine()
1455 termblit(unpack(blTitle))
1456 end
1457 end
1458 termsetCursorBlink(true)
1459 tsv(true)
1460end
1461
1462local logadd = function(name, message, animType, maxFrame, ignoreWrap, _personalID)
1463 log[#log + 1] = {
1464 prefix = name and UIconf.prefix or "",
1465 suffix = name and UIconf.suffix or "",
1466 name = name or "",
1467 message = message or " ",
1468 ignoreWrap = ignoreWrap,
1469 frame = 0,
1470 maxFrame = maxFrame or true,
1471 animType = animType,
1472 personalID = _personalID
1473 }
1474end
1475
1476local logaddTable = function(name, message, animType, maxFrame, ignoreWrap, _personalID)
1477 if type(message) == "table" and type(name) == "string" then
1478 if #message > 0 then
1479 local isGood = true
1480 for l = 1, #message do
1481 if type(message[l]) ~= "string" then
1482 isGood = false
1483 break
1484 end
1485 end
1486 if isGood then
1487 logadd(name, message[1], animType, maxFrame, ignoreWrap, _personalID)
1488 for l = 2, #message do
1489 logadd(nil, message[l], animType, maxFrame, ignoreWrap, _personalID)
1490 end
1491 end
1492 end
1493 end
1494end
1495
1496local enchatSend = function(name, message, option, doLog, animType, maxFrame, crying, recipient, ignoreWrap, omitPersonalID)
1497 option = option or {}
1498 if option.doLog then
1499 if type(message) == "string" then
1500 logadd(name, message, option.animType, option.maxFrame, option.ignoreWrap, (not option.omitPersonalID) and personalID)
1501 else
1502 logaddTable(name, message, option.animType, option.maxFrame, option.ignoreWrap, (not option.omitPersonalID) and personalID)
1503 end
1504 end
1505 local messageID = makeRandomString(64)
1506 local outmsg = encrite({
1507 name = name,
1508 message = message,
1509 animType = option.animType,
1510 maxFrame = option.maxFrame,
1511 messageID = messageID,
1512 recipient = option.recipient,
1513 ignoreWrap = option.ignoreWrap,
1514 personalID = (not option.omitPersonalID) and personalID,
1515 cry = option.crying,
1516 simCommand = option.simCommand,
1517 simArgument = option.simArgument,
1518 })
1519 IDlog[messageID] = true
1520 if not enchat.ignoreModem then
1521 modemTransmit(enchat.port, enchat.port, outmsg)
1522 end
1523 if skynet and enchatSettings.useSkynet then
1524 skynet.send(enchat.skynetPort, outmsg)
1525 end
1526end
1527
1528local cryOut = function(name, crying)
1529 enchatSend(name, nil, {crying = crying})
1530end
1531
1532local getPictureFile = function(path) -- ONLY NFP or NFT, fuck BLT
1533 if not fs.exists(path) then
1534 return false, "No such image."
1535 else
1536 local file = fs.open(path,"r")
1537 local content = file.readAll()
1538 file.close()
1539 local output
1540 if content:find("\31") and content:find("\30") then
1541 output = explode("\n",content:gsub("\31","&"):gsub("\30","~"),nil,false)
1542 else
1543 if content:lower():gsub("[0123456789abcdef\n ]","") ~= "" then
1544 return false, "Invalid image."
1545 else
1546 output = explode("\n",content:gsub("[^\n]","~%1 "),nil,false)
1547 end
1548 end
1549 return output
1550 end
1551end
1552
1553local getTableLength = function(tbl)
1554 local output = 0
1555 for k,v in pairs(tbl) do
1556 output = output + 1
1557 end
1558 return output
1559end
1560
1561local userCryList = {}
1562
1563local commandInit = "/"
1564local commands = {}
1565local simmableCommands = {
1566 big = true
1567}
1568-- Commands only have one argument, being a single string.
1569-- Separate arguments can be extrapolated with the explode() function.
1570commands.about = function()
1571 if enchatSettings.extraNewline then
1572 logadd(nil,nil)
1573 end
1574 logadd(nil,"Enchat "..enchat.version.." by LDDestroier.")
1575 logadd(nil,"'Encrypted, decentralized, &1c&2o&3l&4o&5r&6i&7z&8e&9d&r chat program'")
1576 logadd(nil,"Made in 2018, out of gum and procrastination.")
1577 logadd(nil,nil)
1578 logadd(nil,"AES Lua implementation made by SquidDev.")
1579 logadd(nil,"'Skynet' (enables HTTP chat) belongs to gollark (osmarks).")
1580end
1581commands.exit = function()
1582 enchatSend("*", "'"..yourName.."&}&r~r' buggered off. (disconnect)")
1583 return "exit"
1584end
1585commands.me = function(msg)
1586 if enchatSettings.extraNewline then
1587 logadd(nil,nil)
1588 end
1589 if msg then
1590 enchatSend("&2*", yourName.."~r&2 "..msg, {doLog = true})
1591 else
1592 logadd("*",commandInit.."me [message]")
1593 end
1594end
1595commands.tron = function()
1596 local url = "https://raw.githubusercontent.com/LDDestroier/CC/master/tron.lua"
1597 local prog, contents = http.get(url)
1598 if prog then
1599 enchatSend("*", yourName .. "&}&r~r has started a game of TRON.", {doLog = true})
1600 contents = prog.readAll()
1601 pauseRendering = true
1602 prog = load(contents, nil, nil, _ENV)(enchatSettings.useSkynet and "skynet", "quick", yourName)
1603 else
1604 logadd("*", "Could not download TRON.")
1605 end
1606 pauseRendering = false
1607 doRender = true
1608end
1609commands.colors = function()
1610 if enchatSettings.extraNewline then
1611 logadd(nil,nil)
1612 end
1613 logadd("*", "&{Color codes: (use & or ~)&}")
1614 logadd(nil, " &7~11~22~33~44~55~66~7&87~8&78~99~aa~bb~cc~dd~ee~ff")
1615 logadd(nil, " &{Reset text/BG with &r and ~r.&}")
1616 logadd(nil, " &{Use &k for krazy text.&}")
1617end
1618commands.update = function()
1619 local res, message = updateEnchat()
1620 if res then
1621 enchatSend("*", yourName.."&}&r~r has updated and exited.")
1622 termsetBackgroundColor(colors.black)
1623 termsetTextColor(colors.white)
1624 termclear()
1625 termsetCursorPos(1,1)
1626 print(message)
1627 return "exit"
1628 else
1629 logadd("*", res)
1630 end
1631end
1632commands.picto = function(filename)
1633 local image, output, res
1634 local isEmpty
1635 if filename then
1636 output, res = getPictureFile(filename)
1637 if not output then
1638 logadd("*",res)
1639 logadd(nil,nil)
1640 return
1641 else
1642 tableinsert(output,1,"")
1643 end
1644 else
1645 isEmpty = true
1646 output = {""}
1647 pauseRendering = true
1648 local image = pictochat(26,11)
1649 pauseRendering = false
1650 for y = 1, #image[1] do
1651 output[#output+1] = ""
1652 for x = 1, #image[1][1] do
1653 output[#output] = table.concat({
1654 output[#output],
1655 "&",
1656 image[2][y]:sub(x,x),
1657 "~",
1658 image[3][y]:sub(x,x),
1659 image[1][y]:sub(x,x)
1660 })
1661 isEmpty = isEmpty and (image[1][y]:sub(x,x) == " " and image[3][y]:sub(x,x) == " ")
1662 end
1663 end
1664 end
1665 if not isEmpty then
1666 enchatSend(yourName, output, {doLog = true, animType = "slideFromLeft", ignoreWrap = true})
1667 end
1668end
1669commands.list = function()
1670 userCryList = {}
1671 local tim = os.startTimer(0.5)
1672 cryOut(yourName, true)
1673 while true do
1674 local evt = {os.pullEvent()}
1675 if evt[1] == "timer" then
1676 if evt[2] == tim then
1677 break
1678 end
1679 end
1680 end
1681 if enchatSettings.extraNewline then
1682 logadd(nil,nil)
1683 end
1684 if getTableLength(userCryList) == 0 then
1685 logadd(nil,"Nobody's there.")
1686 else
1687 for k,v in pairs(userCryList) do
1688 logadd(nil,"+'"..k.."'")
1689 end
1690 end
1691end
1692commands.nick = function(newName)
1693 if enchatSettings.extraNewline then
1694 logadd(nil,nil)
1695 end
1696 if newName then
1697 if checkValidName(newName) then
1698 if newName == yourName then
1699 logadd("*","But you're already called that!")
1700 else
1701 enchatSend("*", "'"..yourName.."&}&r~r' is now known as '"..newName.."&}&r~r'.", {doLog = true})
1702 yourName = newName
1703 end
1704 else
1705 if #newName < 2 then
1706 logadd("*", "That name is too damned small.")
1707 elseif #newName > 32 then
1708 logadd("*", "Woah there, that name is too large.")
1709 end
1710 end
1711 else
1712 logadd("*",commandInit.."nick [newName]")
1713 end
1714end
1715commands.whoami = function(now)
1716 if enchatSettings.extraNewline then
1717 logadd(nil,nil)
1718 end
1719 if now == "now" then
1720 logadd("*","You are still '"..yourName.."&}&r~r'!")
1721 else
1722 logadd("*","You are '"..yourName.."&}&r~r'!")
1723 end
1724end
1725commands.key = function(newKey)
1726 if enchatSettings.extraNewline then
1727 logadd(nil,nil)
1728 end
1729 if newKey then
1730 if newKey ~= encKey then
1731 enchatSend("*", "'"..yourName.."&}&r~r' buggered off. (keychange)")
1732 setEncKey(newKey)
1733 logadd("*", "Key changed to '"..encKey.."&}&r~r'.")
1734 enchatSend("*", "'"..yourName.."&}&r~r' has moseyed on over.", {omitPersonalID = true})
1735 else
1736 logadd("*", "That's already the key, though.")
1737 end
1738 else
1739 logadd("*","Key = '"..encKey.."&}&r~r'")
1740 logadd("*","Channel = '"..enchat.port.."'")
1741 end
1742end
1743commands.shrug = function(face)
1744 if enchatSettings.extraNewline then
1745 logadd(nil,nil)
1746 end
1747 enchatSend(yourName, "¯\\_"..(face and ("("..face..")") or "\2").."_/¯", {doLog = true})
1748end
1749commands.asay = function(_argument)
1750 local sPoint = (_argument or ""):find(" ")
1751 if enchatSettings.extraNewline then
1752 logadd(nil,nil)
1753 end
1754 if not sPoint then
1755 logadd("*","Animation types:")
1756 for k,v in pairs(animations) do
1757 logadd(nil," '"..k.."'")
1758 end
1759 else
1760 local animType = _argument:sub(1,sPoint-1)
1761 local message = _argument:sub(sPoint+1)
1762 local animFrameMod = {
1763 flash = 8,
1764 fadeIn = 4,
1765 }
1766 if animations[animType] then
1767 if textToBlit(message,true):gsub(" ","") ~= "" then
1768 enchatSend(yourName, message, {doLog = true, animType = animType, maxFrame = animFrameMod[animType]})
1769 else
1770 logadd("*","That message is no good.")
1771 end
1772 else
1773 logadd("*","Invalid animation type.")
1774 end
1775 end
1776end
1777commands.big = function(_argument, simUser)
1778 local sPoint = (_argument or ""):find(" ")
1779 if enchatSettings.extraNewline then
1780 logadd(nil,nil)
1781 end
1782 if not sPoint then
1783 logadd("*",commandInit .. "big <size> <text>")
1784 else
1785 local fontSize = tonumber(_argument:sub(1,sPoint-1))
1786 local message = _argument:sub(sPoint+1)
1787 if not fontSize then
1788 logadd("*","Size must be number between 0 and 2.")
1789 elseif fontSize < 0 or fontSize > 2 then
1790 logadd("*","Size must be number between 0 and 2.")
1791 else
1792 fontSize = math.floor(.5+fontSize)
1793 local tOutput
1794 if fontSize > 0 then
1795 message = textToBlit(message, false, "0", "f")
1796 local output = {{},{},{}}
1797 local x, y = 1, 1
1798 local char
1799 for i = 1, #message[1] do
1800 char = bigfont.makeBlittleText(
1801 fontSize,
1802 stringsub(message[1],i,i),
1803 stringsub(message[2],i,i),
1804 stringsub(message[3],i,i)
1805 )
1806 x = x + char.width
1807 if x >= scr_x then
1808 y = y + char.height
1809 x = char.width
1810 end
1811 for charY = 1, char.height do
1812 output[1][y+charY-1] = (output[1][y+charY-1] or " ") .. char[1][charY]
1813 output[2][y+charY-1] = (output[2][y+charY-1] or " ") .. char[2][charY]
1814 output[3][y+charY-1] = (output[3][y+charY-1] or " ") .. char[3][charY]
1815 end
1816 end
1817 tOutput = {""}
1818 local yy = 1
1819 for y = 1, #output[1] do
1820 tOutput[#tOutput+1] = ""
1821 for x = 1, #output[1][y] do
1822 tOutput[#tOutput] = table.concat({tOutput[#tOutput],"&",output[2][yy]:sub(x,x),"~",output[3][yy]:sub(x,x),output[1][yy]:sub(x,x)})
1823 end
1824 yy = yy + 1
1825 end
1826 else
1827 tOutput = message
1828 end
1829 if simUser then
1830 logaddTable(simUser, tOutput)
1831 else
1832 logaddTable(yourName, tOutput)
1833 enchatSend(yourName, nil, {simCommand = "big", simArgument = _argument})
1834 end
1835 end
1836 end
1837end
1838commands.msg = function(_argument)
1839 local sPoint = (_argument or ""):find(" ")
1840 if enchatSettings.extraNewline then
1841 logadd(nil,nil)
1842 end
1843 if not sPoint then
1844 logadd("*",commandInit.."msg <recipient> <message>")
1845 else
1846 local recipient = _argument:sub(1,sPoint-1)
1847 local message = _argument:sub(sPoint+1)
1848 if not message then
1849 logadd("*","You got half of the arguments down pat, at least.")
1850 else
1851 if textToBlit(message,true):gsub(" ","") == "" then
1852 logadd("*","That message is no good.")
1853 else
1854 enchatSend(yourName, message, {recipient = recipient})
1855 logadd("*","to '"..recipient.."': "..message)
1856 end
1857 end
1858 end
1859end
1860commands.palette = function(_argument)
1861 local argument = _argument or ""
1862 if enchatSettings.extraNewline then
1863 logadd(nil,nil)
1864 end
1865 if argument:gsub("%s","") == "" then
1866 local buff = ""
1867 for k,v in pairs(palette) do
1868 buff = buff..k..", "
1869 end
1870 buff = buff:sub(1,-3)
1871 logadd("*",commandInit.."palette "..buff.." <colorcode>")
1872 else
1873 argument = explode(" ",argument)
1874 if #argument == 1 then
1875 if argument[1]:gsub("%s",""):lower() == "reset" or argument[1]:gsub("%s",""):lower() == "enchat3" then
1876 palette = {
1877 bg = colors.black,
1878 txt = colors.white,
1879 promptbg = colors.gray,
1880 prompttxt = colors.white,
1881 scrollMeter = colors.lightGray,
1882 chevron = colors.black,
1883 title = colors.lightGray,
1884 titlebg = colors.gray,
1885 }
1886 UIconf = {
1887 promptY = 1,
1888 chevron = ">",
1889 chatlogTop = 1,
1890 title = "Enchat 3",
1891 doTitle = false,
1892 titleY = 1,
1893 nameDecolor = false,
1894 centerTitle = true,
1895 prefix = "<",
1896 suffix = "> "
1897 }
1898 termsetBackgroundColor(palette.bg)
1899 termclear()
1900 logadd("*","You cleansed your palette.")
1901 saveSettings()
1902 elseif argument[1]:gsub("%s",""):lower() == "enchat2" then
1903 palette = {
1904 bg = colors.gray,
1905 txt = colors.white,
1906 promptbg = colors.white,
1907 prompttxt = colors.black,
1908 scrollMeter = colors.white,
1909 chevron = colors.lightGray,
1910 title = colors.yellow,
1911 titlebg = colors.gray,
1912 }
1913 UIconf = {
1914 promptY = 1,
1915 chevron = ">",
1916 chatlogTop = 1,
1917 title = "Enchat 2",
1918 doTitle = false,
1919 titleY = 1,
1920 nameDecolor = false,
1921 centerTitle = false,
1922 prefix = "<",
1923 suffix = "> "
1924 }
1925 termsetBackgroundColor(palette.bg)
1926 termclear()
1927 logadd("*","Switched to the old Enchat2 palette.")
1928 saveSettings()
1929 elseif argument[1]:gsub("%s",""):lower() == "enchat1" then
1930 logadd("*","We don't talk about that one.")
1931 elseif argument[1]:gsub("%s",""):lower() == "enchat4" then
1932 logadd("*","Let's leave that to future LDD.")
1933 elseif argument[1]:gsub("%s",""):lower() == "chat.lua" then
1934 palette = {
1935 bg = colors.black,
1936 txt = colors.white,
1937 promptbg = colors.black,
1938 prompttxt = colors.white,
1939 scrollMeter = colors.white,
1940 chevron = colors.yellow,
1941 title = colors.yellow,
1942 titlebg = colors.black,
1943 }
1944 UIconf = {
1945 promptY = 0,
1946 chevron = ": ",
1947 chatlogTop = 2,
1948 title = "YOURNAME on ENCKEY",
1949 doTitle = true,
1950 titleY = 1,
1951 nameDecolor = true,
1952 centerTitle = true,
1953 prefix = "<",
1954 suffix = "> "
1955 }
1956 termsetBackgroundColor(palette.bg)
1957 termclear()
1958 logadd("*","Switched to /rom/programs/rednet/chat.lua palette.")
1959 saveSettings()
1960 elseif argument[1]:gsub("%s",""):lower() == "talk" then
1961 palette = {
1962 bg = colors.black,
1963 txt = colors.white,
1964 promptbg = colors.black,
1965 prompttxt = colors.white,
1966 scrollMeter = colors.white,
1967 chevron = colors.white,
1968 title = colors.black,
1969 titlebg = colors.white,
1970 }
1971 UIconf = {
1972 promptY = 0,
1973 chevron = "",
1974 chatlogTop = 1,
1975 title = " enchat v3.0 channel: ENCKEY:PORT",
1976 titleY = scr_y - 1,
1977 doTitle = true,
1978 nameDecolor = false,
1979 centerTitle = false,
1980 prefix = "<",
1981 suffix = "> "
1982 }
1983 termsetBackgroundColor(palette.bg)
1984 termclear()
1985 logadd("*","Switched to Talk palette.")
1986 saveSettings()
1987 elseif argument[1]:gsub("%s",""):lower() == "darkchat" then
1988 palette = {
1989 bg = colors.black,
1990 txt = colors.white,
1991 promptbg = colors.black,
1992 prompttxt = colors.white,
1993 scrollMeter = colors.white,
1994 chevron = colors.white,
1995 title = colors.white,
1996 titlebg = colors.blue,
1997 }
1998 UIconf = {
1999 promptY = 0,
2000 chevron = "Message: ",
2001 chatlogTop = 1,
2002 title = "<User: YOURNAME> <Channel: ENCKEY>",
2003 titleY = scr_y - 1,
2004 doTitle = true,
2005 nameDecolor = false,
2006 centerTitle = true,
2007 prefix = "",
2008 suffix = ": "
2009 }
2010 termsetBackgroundColor(palette.bg)
2011 termclear()
2012 logadd("*","Switched to DarkChat palette.")
2013 saveSettings()
2014 else
2015 if not palette[argument[1]] then
2016 logadd("*","There's no such palette option.")
2017 else
2018 logadd("*","'"..argument[1].."' = '"..toblit[palette[argument[1]]].."'")
2019 end
2020 end
2021 else
2022 if #argument > 2 then
2023 argument = {argument[1], table.concat(argument," ",2)}
2024 end
2025 argument[1] = argument[1]:lower()
2026 local newcol = argument[2]:lower()
2027 if not palette[argument[1]] then
2028 logadd("*","That's not a valid palette choice.")
2029 else
2030 if not (tocolors[newcol] or colors_strnames[newcol]) then
2031 logadd("*","That isn't a valid color code. (0-f)")
2032 else
2033 palette[argument[1]] = (tocolors[newcol] or colors_strnames[newcol])
2034 logadd("*","Palette changed.",false)
2035 saveSettings()
2036 end
2037 end
2038 end
2039 end
2040end
2041commands.clear = function()
2042 log = {}
2043 IDlog = {}
2044end
2045commands.ping = function(pong)
2046 if enchatSettings.extraNewline then
2047 logadd(nil,nil)
2048 end
2049 logadd(nil, pong or "Pong!")
2050end
2051commands.set = function(_argument)
2052 if enchatSettings.extraNewline then
2053 logadd(nil,nil)
2054 end
2055 argument = _argument or ""
2056 local collist = {
2057 ["string"] = function() return "0" end,
2058 ["table"] = function() return "5" end,
2059 ["number"] = function() return "0" end,
2060 ["boolean"] = function(val) if val then return "d" else return "e" end end,
2061 ["function"] = function() return "c" end,
2062 ["nil"] = function() return "8" end,
2063 ["thread"] = function() return "d" end,
2064 ["userdata"] = function() return "c" end, -- ha
2065 }
2066 local custColorize = function(input)
2067 return "&"..collist[type(input)](input)
2068 end
2069 local contextualQuote = function(judgetxt,txt)
2070 if type(judgetxt) == "string" then
2071 return table.concat({"'",txt,"'"})
2072 else
2073 return txt
2074 end
2075 end
2076 local arguments = explode(" ",argument)
2077 if #argument == 0 then
2078 for k,v in pairs(enchatSettings) do
2079 logadd(nil,"&4'"..k.."'&r = "..contextualQuote(v,custColorize(v)..tostring(v).."&r"))
2080 end
2081 else
2082 if enchatSettings[arguments[1]] ~= nil then
2083 if #arguments >= 2 then
2084 local newval = table.concat(arguments," ",2)
2085 if tonumber(newval) then
2086 newval = tonumber(newval)
2087 elseif textutilsunserialize(newval) ~= nil then
2088 newval = textutilsunserialize(newval)
2089 end
2090 if type(enchatSettings[arguments[1]]) == type(newval) then
2091 enchatSettings[arguments[1]] = newval
2092 logadd("*","Set '&4"..arguments[1].."&r' to &{"..contextualQuote(newval,textutilsserialize(newval).."&}").." ("..type(newval)..")")
2093 saveSettings()
2094 else
2095 logadd("*","Wrong value type (it's "..type(enchatSettings[arguments[1]])..")")
2096 end
2097 else
2098 logadd("*","'"..arguments[1].."' is set to "..contextualQuote(enchatSettings[arguments[1]],custColorize(enchatSettings[arguments[1]])..textutilsserialize(enchatSettings[arguments[1]]).."&r").." ("..type(enchatSettings[arguments[1]])..")")
2099 end
2100 else
2101 logadd("*","No such setting.")
2102 end
2103 end
2104 if enchatSettings.useSkynet and (not skynet) then
2105 pauseRendering = true
2106 termsetBackgroundColor(colors.black)
2107 termclear()
2108 pauseRendering = false
2109 end
2110end
2111commands.help = function(cmdname)
2112 if enchatSettings.extraNewline then
2113 logadd(nil,nil)
2114 end
2115 if cmdname then
2116 local helpList = {
2117 exit = "Exits Enchat and returns to loader (most likely CraftOS)",
2118 about = "Tells you a bit about this here Enchat.",
2119 me = "Sends a message in the format of \"* yourName message\"",
2120 colors = "Lists all the colors you can use.",
2121 update = "Updates and overwrites Enchat, then exits if successful.",
2122 list = "Lists all users in range using the same key.",
2123 nick = "Give yourself a different username.",
2124 whoami = "Tells you your current username.",
2125 key = "Change the current encryption key. Tells you the key, if without argument.",
2126 clear = "Clears the local chat log. Not your inventory, I swear.",
2127 ping = "Pong. *sigh*",
2128 shrug = "Sends out a shrugging emoticon.",
2129 set = "Changes config options during the current session. Lists all options, if without argument.",
2130 msg = "Sends a message that is only logged by a specific user.",
2131 picto = "Opens an image maker and sends the result. Use the scroll wheel to change color, and hold left shift to change text color. If argument given, will look for an image at the given path and use that instead.",
2132 tron = "Starts up a game of TRON.",
2133 big = "Sends your message, but enlarged by a specified amount via Wojbie's BigFont API.",
2134 help = "Shows every command, or describes a specific command.",
2135 }
2136 cmdname = cmdname:gsub(" ",""):gsub("/","")
2137 if helpList[cmdname] then
2138 logadd("*", helpList[cmdname])
2139 else
2140 if commands[cmdname] then
2141 logadd("*", "No help info for that command.")
2142 else
2143 logadd("*", "No such command to get help for.")
2144 end
2145 end
2146 else
2147 logadd("*","All commands:")
2148 local output = ""
2149 for k,v in pairs(commands) do
2150 output = output.." "..commandInit..k..","
2151 end
2152 logadd(nil, output:sub(1,-2))
2153 end
2154end
2155commandAliases = {
2156 quit = commands.exit,
2157 colours = commands.colors,
2158 ls = commands.list,
2159 cry = commands.list,
2160 nickname = commands.nick,
2161 channel = commands.key,
2162 palate = commands.palette,
2163 tell = commands.msg,
2164 whisper = commands.msg,
2165 ["?"] = commands.help,
2166 porn = function() logadd("*","Yeah, no.") end,
2167 whoareyou = function() logadd("*", "I'm Enchat. But surely, you know this?") end,
2168 fuck = function() logadd("*","A mind is a terrible thing to waste.") end,
2169 hello = function() logadd("*","Hey.") end,
2170 hi = function() logadd("*","Hiya.") end,
2171 hey = function() logadd("*","That's for horses.") end,
2172 bye = function() logadd("*","You know, you can use /exit.") end,
2173 die = function() logadd("*","You wish.") end,
2174 nap = function() logadd("*","The time for napping has passed.") end,
2175 sorry = function() logadd("*","That's okay.") end,
2176 jump = function() logadd("*","Sorry. This program is in a NO JUMPING zone.") end,
2177 enchat = function() logadd("*","At your service!") end,
2178 win = function() logadd("*","Naturally!") end,
2179 lose = function() logadd("*","Preposterous!") end,
2180 xyzzy = function() logadd("*","A hollow voice says \"Fool.\"") end,
2181 wait = function() logadd("*","Time passes...") end,
2182 stop = function() logadd("*","Hammertime!","fadeIn") end,
2183 shit = function() logadd("*","Man, you're telling me!") end,
2184 eat = function() logadd("*","You're not hungry.") end,
2185 what = function() logadd("*","What indeed.") end,
2186 ldd = function() logadd(nil,"& that's me") end,
2187 OrElseYouWill = function()
2188 enchatSend("*", "'"..yourName.."&}&r~r' buggered off. (disconnect)")
2189 error("DIE")
2190 end
2191}
2192
2193local checkIfCommand = function(input)
2194 if input:sub(1,#commandInit) == commandInit then
2195 return true
2196 else
2197 return false
2198 end
2199end
2200
2201local parseCommand = function(input)
2202 local sPos1, sPos2 = input:find(" ")
2203 local cmdName, cmdArgs
2204 if sPos1 then
2205 cmdName = input:sub(#commandInit+1, sPos1-1)
2206 cmdArgs = input:sub(sPos2+1)
2207 else
2208 cmdName = input:sub(#commandInit+1)
2209 cmdArgs = nil
2210 end
2211
2212 local res
2213 local CMD = commands[cmdName] or commandAliases[cmdName]
2214 if CMD then
2215 res = CMD(cmdArgs)
2216 if res == "exit" then
2217 return "exit"
2218 end
2219 else
2220 logadd("*", "No such command.")
2221 end
2222end
2223
2224local main = function()
2225 termsetBackgroundColor(palette.bg)
2226 termclear()
2227 os.queueEvent("render_enchat")
2228 local mHistory = {}
2229
2230 while true do
2231
2232 termsetCursorPos(1, scr_y-UIconf.promptY)
2233 termsetBackgroundColor(palette.promptbg)
2234 termclearLine()
2235 termsetTextColor(palette.chevron)
2236 termwrite(UIconf.chevron)
2237 termsetTextColor(palette.prompttxt)
2238
2239 local input = colorRead(nil, mHistory)
2240 if textToBlit(input,true):gsub(" ","") ~= "" then -- people who send blank messages in chat programs deserve to die
2241 if checkIfCommand(input) then
2242 local res = parseCommand(input)
2243 if res == "exit" then
2244 return "exit"
2245 end
2246 else
2247 if enchatSettings.extraNewline then
2248 logadd(nil,nil,nil,nil,nil,personalID) -- readability is key
2249 end
2250 enchatSend(yourName, input, {doLog = true})
2251 end
2252 if mHistory[#mHistory] ~= input then
2253 mHistory[#mHistory+1] = input
2254 end
2255 elseif input == "" then
2256 logadd(nil,nil,nil,nil,nil,personalID)
2257 end
2258 os.queueEvent("render_enchat")
2259
2260 end
2261
2262end
2263
2264local handleReceiveMessage = function(user, message, animType, maxFrame, _personalID)
2265 if enchatSettings.extraNewline then
2266 logadd(nil,nil,nil,nil,nil,_personalID) -- readability is still key
2267 end
2268 logadd(user, message, animations[animType] and animType or nil, (type(maxFrame) == "number") and maxFrame or nil, nil, _personalID)
2269 os.queueEvent("render_enchat")
2270end
2271
2272local adjScroll = function(distance)
2273 scroll = mathmin(maxScroll, mathmax(0, scroll + distance))
2274end
2275
2276local checkRSinput = function()
2277 return (
2278 rs.getInput("front") or
2279 rs.getInput("back") or
2280 rs.getInput("left") or
2281 rs.getInput("right") or
2282 rs.getInput("top") or
2283 rs.getInput("bottom")
2284 )
2285end
2286
2287local handleEvents = function()
2288 local oldScroll
2289 local keysDown = {}
2290 while true do
2291 local evt = {os.pullEvent()}
2292 if evt[1] == "enchat_receive" then
2293 if type(evt[2]) == "string" and type(evt[3]) == "string" then
2294 handleReceiveMessage(evt[2], evt[3])
2295 end
2296 elseif evt[1] == "chat" and ((not checkRSinput()) or (not enchat.disableChatboxWithRedstone)) then
2297 if enchat.useChatbox then
2298 if enchatSettings.extraNewline then
2299 logadd(nil,nil) -- readability is key
2300 end
2301 enchatSend(evt[2], evt[3], {doLog = true})
2302 end
2303 elseif evt[1] == "chat_message" and ((not checkRSinput()) or (not enchat.disableChatboxWithRedstone)) then -- computronics
2304 if enchat.useChatbox then
2305 if enchatSettings.extraNewline then
2306 logadd(nil,nil) -- readability is key
2307 end
2308 enchatSend(evt[3], evt[4], {doLog = true})
2309 end
2310 elseif (evt[1] == "modem_message") or (evt[1] == "skynet_message" and enchatSettings.useSkynet) then
2311 local side, freq, repfreq, msg, distance
2312 if evt[1] == "modem_message" then
2313 side, freq, repfreq, msg, distance = evt[2], evt[3], evt[4], evt[5], evt[6]
2314 else
2315 freq, msg = evt[2], evt[3]
2316 end
2317 if (freq == enchat.port) or (freq == enchat.skynetPort) then
2318 msg = decrite(msg)
2319 if type(msg) == "table" then
2320 if (type(msg.name) == "string") then
2321 if #msg.name <= 32 then
2322 if msg.messageID and (not IDlog[msg.messageID]) then
2323 userCryList[msg.name] = true
2324 IDlog[msg.messageID] = true
2325 if ((not msg.recipient) or (msg.recipient == yourName or msg.recipient == textToBlit(yourName,true))) then
2326 if type(msg.message) == "string" then
2327 handleReceiveMessage(msg.name, tostring(msg.message), msg.animType, msg.maxFrame, msg.personalID)
2328 if chatbox and enchat.useChatbox and ((not checkRSinput()) or (not enchat.disableChatboxWithRedstone)) then
2329 chatbox.say(UIconf.prefix .. msg.name .. UIconf.suffix .. msg.message, msg.name)
2330 end
2331 elseif type(msg.message) == "table" and enchatSettings.acceptPictoChat and #msg.message <= 64 then
2332 logaddTable(msg.name, msg.message, msg.animType, msg.maxFrame, msg.ignoreWrap, msg.personalID)
2333 if enchatSettings.extraNewline then
2334 logadd(nil,nil)
2335 end
2336 elseif commands[msg.simCommand or false] and type(msg.simArgument) == "string" then
2337 if simmableCommands[msg.simCommand or false] then
2338 commands[msg.simCommand](msg.simArgument, msg.name)
2339 end
2340 end
2341 end
2342 if (msg.cry == true) then
2343 cryOut(yourName, false)
2344 end
2345 end
2346 end
2347 end
2348 end
2349 end
2350 elseif evt[1] == "mouse_scroll" and (not pauseRendering) then
2351 local dist = evt[2]
2352 oldScroll = scroll
2353 adjScroll(enchatSettings.reverseScroll and -dist or dist)
2354 if scroll ~= oldScroll then
2355 dab(renderChat)
2356 end
2357 elseif evt[1] == "key" and (not pauseRendering) then
2358 local key = evt[2]
2359 keysDown[key] = true
2360 oldScroll = scroll
2361 local pageSize = (scr_y-UIconf.promptY) - UIconf.chatlogTop
2362 if key == keys.pageUp then
2363 adjScroll(-(keysDown[keys.leftCtrl] and pageSize or enchatSettings.pageKeySpeed))
2364 elseif key == keys.pageDown then
2365 adjScroll(keysDown[keys.leftCtrl] and pageSize or enchatSettings.pageKeySpeed)
2366 end
2367 if scroll ~= oldScroll then
2368 dab(renderChat)
2369 end
2370 elseif evt[1] == "key_up" then
2371 local key = evt[2]
2372 keysDown[key] = nil
2373 elseif (evt[1] == "render_enchat") and (not pauseRendering) then
2374 dab(renderChat)
2375 elseif (evt[1] == "tron_complete") then
2376 if evt[3] then
2377 if enchatSettings.extraNewline then
2378 logadd(nil,nil)
2379 end
2380 if evt[2] == "win" then
2381 enchatSend("*", yourName .. "&}&r~r beat " .. (evt[4] or "someone") .. "&}&r~r in TRON!", {doLog = true})
2382 elseif evt[2] == "lose" then
2383 enchatSend("*", (evt[4] or "Someone") .. "&}&r~r beat " .. yourName .. "&}&r~r in TRON!", {doLog = true})
2384 elseif evt[2] == "tie" then
2385 enchatSend("*", yourName .. "&}&r~r tied with " .. (evt[4] or "someone") .. "&}&r~r in TRON!", {doLog = true})
2386 end
2387 elseif evt[2] == "timeout" then
2388 if enchatSettings.extraNewline then
2389 logadd(nil,nil)
2390 end
2391 enchatSend("*", yourName .. "&}&r~r timed out against " .. (evt[4] or "someone") .. "&}&r~r in TRON...", {doLog = true})
2392 end
2393 elseif evt[1] == "terminate" then
2394 return "exit"
2395 end
2396 end
2397end
2398
2399local keepRedrawing = function()
2400 while true do
2401 sleep(enchatSettings.redrawDelay)
2402 if not pauseRendering then
2403 os.queueEvent("render_enchat")
2404 end
2405 end
2406end
2407
2408local handleNotifications = function()
2409 while true do
2410 os.pullEvent("render_enchat")
2411 if canvas and enchatSettings.doNotif then
2412 notif.displayNotifications(true)
2413 end
2414 end
2415end
2416
2417getModem()
2418
2419enchatSend("*", "'"..yourName.."&}&r~r' has moseyed on over.", {doLog = true, omitPersonalID = true})
2420
2421local funky = {
2422 main,
2423 handleEvents,
2424 keepRedrawing,
2425 handleNotifications
2426}
2427
2428if skynet then
2429 funky[#funky+1] = function()
2430 while true do
2431 if skynet then
2432 pcall(skynet.listen)
2433 local success, msg = pcall(skynet.open, enchat.skynetPort)
2434 if not success then
2435 skynet = nil
2436 end
2437 end
2438 sleep(5)
2439 end
2440 end
2441end
2442
2443pauseRendering = false
2444
2445local res, outcome = pcall(function()
2446 return parallel.waitForAny(unpack(funky))
2447end)
2448
2449os.pullEvent = oldePullEvent
2450if skynet then
2451 if skynet.socket then
2452 skynet.socket.close()
2453 end
2454end
2455
2456if canvas then
2457 canvas.clear()
2458end
2459
2460tsv(true) -- in case it's false, y'know
2461
2462if not res then
2463 prettyClearScreen(1,scr_y-1)
2464 termsetTextColor(colors.white)
2465 termsetBackgroundColor(colors.gray)
2466 cwrite("There was an error.",2)
2467 cfwrite("Report this to &3@LDDestroier#2901&r",3)
2468 cwrite("on Discord,",4)
2469 cwrite("if you feel like it.",5)
2470 termsetCursorPos(1,7)
2471 printError(outcome)
2472 termsetTextColor(colors.lightGray)
2473 cwrite("I'll probably fix it, maybe.",10)
2474end
2475
2476termsetCursorPos(1, scr_y)
2477termsetBackgroundColor(initcolors.bg)
2478termsetTextColor(initcolors.txt)
2479termclearLine()
2480