2024-01-28 13:56:31 +03:00

886 lines
29 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

local screen = require("Screen")
local GUI = require("GUI")
local color = require("Color")
local filesystem = require("Filesystem")
local system = require("System")
local paths = require("Paths")
local system = require("System")
local text = require("Text")
local event = require("Event")
local number = require("Number")
-------------------------------------------------------------------------------
local socketHandle
local socketConnectDelay = 1
local socketReadDelay = 1
local oldUptime = 0
local channelUsersList = {operators = {}, voiced = {}, default = {}}
local channelUsersListUpdatingFinished = true
local userZoneMaxWidth = 26
local chatTimeWidth = 5
local selectedItem
local socketUsername = "Socket "
local systemNames = {
["NickServ"] = true,
["ChanServ"] = true,
["DerpServ"] = true,
[socketUsername] = true,
}
local colorScheme = {
noticeMessageSender = 0x0049BF,
noticeMessageText = 0x0092FF,
actionMessageSender = 0xCC9200,
actionMessageText = 0xFFB600,
myMessageSender = 0x2D2D2D,
myMessageText = 0x969696,
otherMessageSender = 0x2D2D2D,
otherMessageText = 0x696969,
channelDataMessageSender = 0x696969,
channelDataMessageText = 0xB4B4B4,
}
local applicationPath = paths.user.applicationData .. "IRC/"
local configPath = applicationPath .. "Config.cfg"
local historyPath = applicationPath .. "History.cfg"
local config = {
server = "irc.esper.net",
port = 6667,
username = "",
password = "",
historyLimit = 50,
}
local history = {
["#cc.ru"] = {},
["#oc"] = {},
[socketUsername] = {},
["NickServ"] = {},
["ECS"] = {},
}
if filesystem.exists(configPath) then
config = filesystem.readTable(configPath)
end
if filesystem.exists(historyPath) then
history = filesystem.readTable(historyPath)
end
-------------------------------------------------------------------------------
local workspace, window = system.addWindow(GUI.filledWindow(1, 1, 110, 27, 0xE1E1E1))
local leftPanel = window:addChild(GUI.panel(1, 1, 21, 1, 0x2D2D2D))
local leftLayout = window:addChild(GUI.layout(1, 4, leftPanel.width, 1, 1, 1))
leftLayout:setAlignment(1, 1, GUI.ALIGNMENT_HORIZONTAL_LEFT, GUI.ALIGNMENT_VERTICAL_TOP)
leftLayout:setMargin(1, 1, 0, 0)
local channelsText = leftLayout:addChild(GUI.text(1, 1, 0xE1E1E1, " Channels"))
local channelsList = leftLayout:addChild(GUI.list(1, 1, leftPanel.width, 1, 1, 0, 0x2D2D2D, 0x696969, 0x2D2D2D, 0x696969, 0x3366CC, 0xE1E1E1, false))
local usersText = leftLayout:addChild(GUI.text(1, 1, 0xE1E1E1, " Users"))
local usersList = leftLayout:addChild(GUI.list(1, 1, leftPanel.width, 1, 1, 0, 0x2D2D2D, 0x696969, 0x2D2D2D, 0x696969, 0x3366CC, 0xE1E1E1, false))
local systemText = leftLayout:addChild(GUI.text(1, 1, 0xE1E1E1, " System"))
local systemList = leftLayout:addChild(GUI.list(1, 1, leftPanel.width, 1, 1, 0, 0x2D2D2D, 0x696969, 0x2D2D2D, 0x696969, 0x3366CC, 0xE1E1E1, false))
local contactLayout = window:addChild(GUI.layout(1, 1, leftPanel.width, 3, 1, 1))
contactLayout:setAlignment(1, 1, GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP)
contactLayout:setDirection(1, 1, GUI.DIRECTION_HORIZONTAL)
contactLayout:setSpacing(1, 1, 0)
local contactButtonWidth = 7
local contactAddButton = contactLayout:addChild(GUI.button(1, 1, contactButtonWidth, contactLayout.height, 0x3C3C3C, 0xA5A5A5, 0x878787, 0xB4B4B4, "+"))
local contactRemoveButton = contactLayout:addChild(GUI.button(1, 1, contactButtonWidth, contactLayout.height, 0x4B4B4B, 0xA5A5A5, 0x878787, 0xB4B4B4, "-"))
contactRemoveButton.colors.disabled.background = 0x4B4B4B
contactRemoveButton.colors.disabled.text = 0x696969
local settingsButton = contactLayout:addChild(GUI.button(1, 1, contactButtonWidth, contactLayout.height, 0x3C3C3C, 0xA5A5A5, 0x878787, 0xB4B4B4, "*"))
window.backgroundPanel.width = 18
local rightLayout = window:addChild(GUI.layout(1, 1, window.backgroundPanel.width - 2, 1, 1, 1))
rightLayout:setAlignment(1, 1, GUI.ALIGNMENT_HORIZONTAL_LEFT, GUI.ALIGNMENT_VERTICAL_TOP)
rightLayout:setMargin(1, 1, 0, 0)
rightLayout:setSpacing(1, 1, 0)
local chat = window:addChild(GUI.object(1, 1, 1, 1))
chat.blockScreenEvents = false
local scrollBar = window:addChild(GUI.scrollBar(1, 1, 1, 1, 0xD2D2D2, 0x878787, 1, 1, 1, 1, 1, true))
local chatInputLayout = window:addChild(GUI.layout(1, 1, 1, 3, 1, 1))
chatInputLayout:setAlignment(1, 1, GUI.ALIGNMENT_HORIZONTAL_LEFT, GUI.ALIGNMENT_VERTICAL_TOP)
chatInputLayout:setDirection(1, 1, GUI.DIRECTION_HORIZONTAL)
chatInputLayout:setSpacing(1, 1, 0)
local chatInput = chatInputLayout:addChild(GUI.input(1, 1, 36, 3, 0xE1E1E1, 0x696969, 0xA5A5A5, 0xE1E1E1, 0x2D2D2D, "", "Type message here"))
chatInput.historyEnabled = true
local chatComboBox = chatInputLayout:addChild(GUI.comboBox(1, 1, 14, 3, 0xD2D2D2, 0x4B4B4B, 0xD2D2D2, 0x969696))
chatComboBox.dropDownMenu.itemHeight = 1
chatComboBox:addItem("/msg")
chatComboBox:addItem("/me")
chatComboBox:addItem("/notice")
chatComboBox:addItem("/ctcp")
chatComboBox:addItem("/raw")
local backgroundContainer = window:addChild(GUI.container(1, 1, 1, 1))
local backgroundPanel = backgroundContainer:addChild(GUI.panel(1, 1, 1, 1, 0xF0F0F0))
local backgroundLayout = backgroundContainer:addChild(GUI.layout(1, 1, 1, 1, 1, 1))
backgroundLayout.hidden = true
local loginServerLayout = GUI.layout(1, 1, 36, 3, 1, 1)
loginServerLayout:setAlignment(1, 1, GUI.ALIGNMENT_HORIZONTAL_LEFT, GUI.ALIGNMENT_VERTICAL_TOP)
loginServerLayout:setDirection(1, 1, GUI.DIRECTION_HORIZONTAL)
local loginServerInput = loginServerLayout:addChild(GUI.input(1, 1, 27, 3, 0xE1E1E1, 0x787878, 0xB4B4B4, 0xE1E1E1, 0x2D2D2D, config.server or "", "Server"))
local loginPortInput = loginServerLayout:addChild(GUI.input(1, 1, loginServerLayout.width - loginServerInput.width - 1, 3, 0xE1E1E1, 0x787878, 0xB4B4B4, 0xE1E1E1, 0x2D2D2D, config.port and tostring(config.port) or "", "Port"))
local loginUsernameInput = GUI.input(1, 1, 36, 3, 0xE1E1E1, 0x787878, 0xB4B4B4, 0xE1E1E1, 0x2D2D2D, config.username or "", "Username")
local loginPasswordInput = GUI.input(1, 1, 36, 3, 0xE1E1E1, 0x787878, 0xB4B4B4, 0xE1E1E1, 0x2D2D2D, config.password or "", "Password")
local loginPasswordSwitchAndLabel = GUI.switchAndLabel(1, 1, 36, 8, 0x66DB80, 0xE1E1E1, 0xFFFFFF, 0xB4B4B4, "Log into NickServ:", false)
local loginSubmitButton = GUI.button(1, 1, 36, 3, 0xC3C3C3, 0xFFFFFF, 0x969696, 0xFFFFFF, "Connect")
local loginStatusText = GUI.text(1, 1, 0xA5A5A5, "")
window.actionButtons.localX = 3
window.actionButtons:moveToFront()
local function stringHash(string)
local hash = 0
for i = 1, string.len (string) do
hash = hash + string.byte (string:sub (i, i))
end
return hash
end
local function generateUniqueColor(string)
local safeColors = {
0x9900FF,
0x990040,
0xCC0000,
0xFF6D00,
0x33DB00,
0x33DB80,
0x99B6B6,
0x006D80,
0x0092FF,
0x6692BF,
0x002440,
0x3349FF,
0x666DBF,
0x6624FF,
0x3399BF,
0xCC24FF,
0x660080,
0xCC0080
}
math.randomseed (stringHash (tostring (string)))
return safeColors[math.random (9999999) % #safeColors + 1]
end
local function getProperList(name)
return systemNames[name] and systemList or name:sub(1, 1) == "#" and channelsList or usersList
end
local function getItemByText(name)
return getProperList(name):getItem(name)
end
local function saveConfig()
filesystem.writeTable(configPath, config)
end
local function socketWrite(data)
local success, result = pcall(socketHandle.write, data .. "\r\n")
if success then
return true, result
else
return false, result
end
end
local function sendMessage(target, text, notice, ctcp)
socketWrite((notice and "NOTICE " or "PRIVMSG ") .. target .. (ctcp and " :\x01" or " :") .. text .. (ctcp and "\x01" or ""))
end
local function status(text)
local state = text ~= nil
if text then
loginStatusText.text = text
loginStatusText:update()
end
loginStatusText.hidden, loginServerLayout.hidden, loginUsernameInput.hidden, loginPasswordSwitchAndLabel.hidden, loginSubmitButton.hidden = not state, state, state, state, state
loginPasswordInput.hidden = state and true or not loginPasswordSwitchAndLabel.switch.state
workspace:draw()
end
local function updateLeftLayout()
local function checkTextAndList(text, list)
text.hidden = #list.children == 0
list.hidden = text.hidden
list.height = #list.children
end
checkTextAndList(channelsText, channelsList)
checkTextAndList(usersText, usersList)
checkTextAndList(systemText, systemList)
end
local function contactItemDraw(item)
local background = item.pressed and item.colors.pressed.background or item.colors.default.background
local foreground = item.pressed and item.colors.pressed.text or item.colors.default.text
screen.drawRectangle(item.x, item.y, item.width, item.height, background, foreground, " ")
local y, textLimit = math.floor(item.y + item.height / 2)
if not item.pressed and item.unreadCount > 0 then
local tipString = tostring(item.unreadCount)
local tipWidth = #tipString + 2
screen.drawRectangle(item.x + item.width - tipWidth - 1, y, tipWidth, 1, 0x3C3C3C, 0xE1E1E1, " ")
screen.drawText(item.x + item.width - tipWidth, y, 0xE1E1E1, tipString)
textLimit = item.width - 4 - tipWidth
else
textLimit = item.width - 4
end
screen.drawText(item.x + 2, y, foreground, text.limit(item.text, textLimit, "right"))
end
local function addContactItemToList(name)
if not history[name] then
history[name] = {}
end
local list = getProperList(name)
local item = list:addItem(name)
item.unreadCount = 0
item.draw = contactItemDraw
item.onTouch = function()
if list == channelsList then
usersList.selectedItem = nil
systemList.selectedItem = nil
elseif list == usersList then
channelsList.selectedItem = nil
systemList.selectedItem = nil
else
channelsList.selectedItem = nil
usersList.selectedItem = nil
end
selectedItem = item
item.unreadCount = 0
contactRemoveButton.disabled = systemNames[item.text]
rightLayout.cells[1][1].verticalMargin = 0
scrollBar.maximumValue = #history[name]
scrollBar.value = scrollBar.maximumValue
rightLayout:removeChildren()
workspace:draw()
if socketHandle then
if list == channelsList then
if item.joined then
socketWrite("NAMES " .. name)
else
item.joined = true
socketWrite("JOIN " .. name)
end
end
end
end
table.sort(list.children, function(a, b) return a.text:lower() < b.text:lower() end)
updateLeftLayout()
return item
end
local function addChatMessage(conversationName, text, sender)
if not history[conversationName] then
addContactItemToList(conversationName)
end
text = text:gsub("[\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F]+", "")
local chars = {}
for i = 1, unicode.len(text) do
chars[i] = unicode.sub(text, i, i)
if unicode.isWide(chars[i]) then
chars[i] = "?"
end
end
text = table.concat(chars)
local message = {
text = text,
time = os.date("%H:%M", system.getTime()),
sender = sender,
color = generateUniqueColor(sender)
}
table.insert(history[conversationName], message)
if #history[conversationName] > config.historyLimit then
table.remove(history[conversationName], 1)
end
local item = getItemByText(conversationName)
if item == selectedItem then
scrollBar.maximumValue = #history[conversationName]
if scrollBar.value == scrollBar.maximumValue - 1 then
scrollBar.value = scrollBar.maximumValue
end
elseif item.unreadCount < config.historyLimit then
item.unreadCount = item.unreadCount + 1
end
return message
end
chat.draw = function()
screen.drawRectangle(chat.x, chat.y, chat.width, chat.height, 0xF0F0F0, colorScheme.otherMessageText, " ")
if history[selectedItem.text] and #history[selectedItem.text] > 0 then
local y, userZoneWidth, messages = chat.y + chat.height - 2, 0, history[selectedItem.text]
for i = scrollBar.value, 1, -1 do
userZoneWidth = math.max(userZoneWidth, unicode.len(messages[i].sender or selectedItem.text))
end
userZoneWidth = math.min(userZoneWidth + chatTimeWidth + 4, userZoneMaxWidth)
for i = chat.y, chat.y + chat.height - 1 do
local index = screen.getIndex(chat.x + userZoneWidth - 1, i)
local background, foreground, symbol = screen.rawGet(index)
screen.rawSet(index, background, 0xE1E1E1, "")
end
for i = scrollBar.value, 1, -1 do
if messages[i].newMessages then
local elda = " New messages "
local pizda = chat.width - userZoneWidth - 3 - unicode.len(elda)
local klitor = math.floor(pizda / 2)
local vagina = pizda - klitor
screen.drawText(chat.x + userZoneWidth + 1, y, 0xFF9280, string.rep("", klitor) .. elda .. string.rep("", vagina))
y = y - 1
else
local wrappedLines = text.wrap(messages[i].text, chat.width - userZoneWidth - 3)
local senderColor, textColor
if messages[i].notice then
senderColor, textColor = colorScheme.noticeMessageSender, colorScheme.noticeMessageText
elseif messages[i].action then
senderColor, textColor = colorScheme.actionMessageSender, colorScheme.actionMessageText
elseif messages[i].channelAction then
senderColor, textColor = colorScheme.channelDataMessageSender, colorScheme.channelDataMessageText
else
if not messages[i].color then -- В исходной версии приложухи, в History.cfg цвет не прописан
messages[i].color = generateUniqueColor (messages[i].sender)
end
senderColor = messages[i].color
if messages[i].sender == config.username then
textColor = colorScheme.myMessageText
else
textColor = colorScheme.otherMessageText
end
end
for j = #wrappedLines, 1, -1 do
screen.drawText(chat.x + userZoneWidth + 1, y, textColor, wrappedLines[j])
y = y - 1
if y < chat.y then
return
end
end
local sender = messages[i].sender or selectedItem.text
local limited = text.limit(sender, userZoneWidth - chatTimeWidth - 4, "center")
screen.drawText(chat.x + userZoneWidth - unicode.len(limited) - 2, y + 1, senderColor, limited)
screen.drawText(chat.x + 1, y + 1, 0xC3C3C3, messages[i].time)
end
y = y - 1
end
end
end
local function checkLoginInputs()
loginSubmitButton.disabled = #loginUsernameInput.text == 0 or #loginServerInput.text == 0 or not loginPortInput.text:match("^%d+$")
end
loginUsernameInput.onInputFinished = function()
checkLoginInputs()
workspace:draw()
end
loginPasswordInput.onInputFinished = loginUsernameInput.onInputFinished
loginServerInput.onInputFinished = loginUsernameInput.onInputFinished
loginPortInput.onInputFinished = loginUsernameInput.onInputFinished
local function login()
backgroundLayout.hidden = false
backgroundLayout:removeChildren()
backgroundLayout:addChild(loginServerLayout)
backgroundLayout:addChild(loginUsernameInput)
backgroundLayout:addChild(loginPasswordInput)
backgroundLayout:addChild(loginPasswordSwitchAndLabel)
backgroundLayout:addChild(loginSubmitButton)
backgroundLayout:addChild(loginStatusText)
checkLoginInputs()
status()
end
chatInput.onInputFinished = function()
if #chatInput.text > 0 then
if chatComboBox.selectedItem == 1 then
sendMessage(selectedItem.text, chatInput.text)
addChatMessage(selectedItem.text, chatInput.text, config.username)
elseif chatComboBox.selectedItem == 2 then
sendMessage(selectedItem.text, "ACTION " .. chatInput.text, false, true)
addChatMessage(selectedItem.text, chatInput.text, config.username, "!").action = true
elseif chatComboBox.selectedItem == 3 then
sendMessage(selectedItem.text, chatInput.text, true)
addChatMessage(selectedItem.text, chatInput.text, config.username, "!").notice = true
elseif chatComboBox.selectedItem == 3 then
sendMessage(selectedItem.text, chatInput.text, false, true)
addChatMessage(selectedItem.text, chatInput.text, config.username, "!").action = true
else
socketWrite(chatInput.text)
addChatMessage(selectedItem.text, chatInput.text, config.username)
end
chatInput.text = ""
workspace:draw()
end
end
local function addScrollEventHandler(layout)
local function getTotalHeight(layout)
local height = 0
for i = 1, #layout.children do
height = height + layout.children[i].height + (i < #layout.children and layout.cells[1][1].spacing or 0)
end
return height
end
layout.eventHandler = function(workspace, layout, e1, e2, e3, e4, e5)
if e1 == "scroll" then
if e5 > 0 then
if layout.cells[1][1].verticalMargin < 0 then
layout.cells[1][1].verticalMargin = layout.cells[1][1].verticalMargin + 1
workspace:draw()
end
else
if layout.cells[1][1].verticalMargin > -getTotalHeight(layout) + 1 then
layout.cells[1][1].verticalMargin = layout.cells[1][1].verticalMargin - 1
workspace:draw()
end
end
end
end
end
addScrollEventHandler(leftLayout)
addScrollEventHandler(rightLayout)
local function selectItem(item)
selectedItem = item
item.parent.selectedItem = item:indexOf()
item.onTouch()
end
local function userButtonOnTouch(workspace, object)
local text = object.text:gsub("^[@+]", "")
if history[text] then
selectItem(getItemByText(text))
else
selectItem(addContactItemToList(text))
end
end
local function updateUsersLayoutFromList()
local function addCategory(field, name)
if #channelUsersList[field] > 0 then
rightLayout:addChild(GUI.object(1, 1, 1, 1))
rightLayout:addChild(GUI.text(1, 1, 0x5A5A5A, name)).height = 2
table.sort(channelUsersList[field], function(a, b) return unicode.lower(a) < unicode.lower(b) end)
for i = 1, #channelUsersList[field] do
rightLayout:addChild(GUI.adaptiveButton(1, 1, 0, 0, nil, 0x969696, nil, 0x2D2D2D, channelUsersList[field][i])).onTouch = userButtonOnTouch
end
end
end
rightLayout:removeChildren()
addCategory("operators", "Operators")
addCategory("voiced", "Voiced")
addCategory("default", "Users")
end
local function addUserToList(name)
local firstSymbol = name:sub(1, 1)
local field = firstSymbol == "@" and "operators" or firstSymbol == "+" and "voiced" or "default"
for i = 1, #channelUsersList[field] do
if channelUsersList[field][i] == name then
return true
end
end
table.insert(channelUsersList[field], name)
end
local function removeUserFromList(name)
for field in pairs(channelUsersList) do
for i = 1, #channelUsersList[field] do
if channelUsersList[field][i] == name then
table.remove(channelUsersList[field], i)
updateUsersLayoutFromList()
return true
end
end
end
end
chat.eventHandler = function(workspace, chat, e1, e2, e3, e4, e5)
if e1 == "scroll" then
if e5 > 0 then
if scrollBar.value > 1 then
scrollBar.value = scrollBar.value - 1
workspace:draw()
end
else
if scrollBar.value < scrollBar.maximumValue then
scrollBar.value = scrollBar.value + 1
workspace:draw()
end
end
elseif not e1 and socketHandle and computer.uptime() - oldUptime > socketReadDelay then
local data = ""
while true do
local success, result = pcall(socketHandle.read, math.huge)
if success then
if result and #result > 0 then
data = data .. result
elseif #data > 0 then
for line in data:gmatch("[^\r\n]+") do
addChatMessage(socketUsername, line)
local lineWithoutDots = line:match("^:(.+)")
if lineWithoutDots then
local words = {}
for word in lineWithoutDots:gmatch("[^%s]+") do
table.insert(words, word)
end
if #words >= 2 then
local beforeDots, afterDots = lineWithoutDots:match("^([^:]+):(.+)")
local username, address = words[1]:match("([^!]+)!([^!]+)")
if words[2] == "NOTICE" then
if username then
if username == "NickServ" and afterDots then
if afterDots:match("You are now identified") then
backgroundContainer.hidden = true
elseif afterDots:match("Invalid password for") then
GUI.alert("Invalid password")
end
end
addChatMessage(selectedItem.text, afterDots, username).notice = true
else
if afterDots and afterDots:match("Looking up your hostname...") then
socketWrite("USER " .. config.username .. " 0 * :" .. config.username .. "\r\nNICK " .. config.username)
end
end
elseif words[2] == "PRIVMSG" and username then
local conversationName = words[3]:sub(1, 1) == "#" and words[3] or username
local ctcp = afterDots:match("^\1(.+)\1$")
if ctcp then
local command, data = ctcp:match("^([^%s]+)%s(.+)")
if ctcp == "VERSION" then
sendMessage(username, "VERSION MineOS IRC Client / OpenComputers (Lua 5.3)", true, true)
elseif ctcp == "TIME" then
sendMessage(username, "TIME " .. os.date(system.getTime()), true, true)
elseif command == "PING" then
sendMessage(username, "PING " .. data, true, true)
elseif command == "ACTION" then
addChatMessage(conversationName, username .. " " .. data, "*").action = true
else
message = "Unsupported CTCP command: " .. ctcp
end
-- Regular message
else
addChatMessage(conversationName, afterDots, username)
end
if config.soundNotifications then
computer.beep(2000, 0.05)
end
elseif words[2] == "JOIN" and username then
if selectedItem.text == words[3] then
if not addUserToList(username) then
updateUsersLayoutFromList()
end
end
addChatMessage(words[3], username .. " (" .. address .. ") has joined the channel", "!").channelAction = true
elseif words[2] == "NICK" and username then
addChatMessage(selectedItem.text, username .. " is now known as " .. afterDots, "!").channelAction = true
local item = getItemByText(username)
if item then
history[item.text] = afterDots
item.text = afterDots
end
elseif words[2] == "MODE" and username then
addChatMessage(words[3], username .. " sets " .. words[5] .. " mode to " .. words[4], "!").channelAction = true
elseif (words[2] == "QUIT" or words[2] == "PART") and username then
if removeUserFromList(username) and words[2] == "QUIT" then
addChatMessage(selectedItem.text, username .. " (" .. address .. ") has quit (" .. afterDots .. ")", "!").channelAction = true
end
if words[2] == "PART" and username ~= config.username then
addChatMessage(words[3], username .. " (" .. address .. ") has left the channel", "!").channelAction = true
end
-- User list
elseif words[2] == "353" then
if channelUsersListUpdatingFinished then
channelUsersList.operators, channelUsersList.voiced, channelUsersList.default = {}, {}, {}
channelUsersListUpdatingFinished = false
end
for username in afterDots:gmatch("[^%s]+") do
addUserToList(username)
end
-- User list finish
elseif words[2] == "366" then
channelUsersListUpdatingFinished = true
updateUsersLayoutFromList()
-- No password authorization
elseif words[2] == "001" then
if loginPasswordSwitchAndLabel.switch.state then
status("Waiting for login request...")
else
backgroundContainer.hidden = true
end
-- Nickname in use
elseif words[2] == "433" then
status()
GUI.alert("Nickname is already in use")
-- Channel topic
elseif words[2] == "332" then
addChatMessage(words[4], afterDots, ">").channelAction = true
-- Wait a while
elseif words[2] == "263" then
addChatMessage(selectedItem.text, afterDots, "!").channelAction = true
-- End of MOTD
elseif words[2] == "376" then
if loginPasswordSwitchAndLabel.switch.state then
status("Logging in...")
socketWrite("IDENTIFY " .. config.username .. " " .. config.password)
end
-- No such nick/channel
elseif words[2] == "401" then
addChatMessage(words[4], "There's no " .. (words[4]:sub(1, 1) == "#" and "channel with such name" or "user with such nick") .. " online", "!").channelAction = true
end
end
else
local ping = line:match("^PING( :.+)")
if ping then
socketWrite("PONG" .. ping)
end
end
end
workspace:draw()
break
else
break
end
else
GUI.alert("Failed to read data from socket: " .. tostring(result))
break
end
end
oldUptime = computer.uptime()
end
end
loginSubmitButton.onTouch = function()
config.server = loginServerInput.text
config.port = tonumber(loginPortInput.text)
config.username = loginUsernameInput.text
config.password = loginPasswordInput.text
saveConfig()
status("Connecting to server socket...")
local result, reason = component.get("internet").connect(config.server, config.port)
if result then
socketHandle = result
repeat
event.sleep(socketConnectDelay)
until socketHandle.finishConnect()
status("Connection estabilished, waiting for response...")
socketWrite("")
else
GUI.alert("Failed to connect to server: " .. tostring(socketHandle))
end
end
settingsButton.onTouch = function()
backgroundContainer.hidden = false
backgroundLayout:removeChildren()
local slider = backgroundLayout:addChild(GUI.slider(1, 1, 36, 0x66DB80, 0xE1E1E1, 0xFFFFFF, 0xB4B4B4, 0, 500, config.historyLimit, false, "History limit: ", ""))
slider.height = 2
slider.roundValues = true
local switchAndLabel = backgroundLayout:addChild(GUI.switchAndLabel(1, 1, 36, 8, 0x66DB80, 0xE1E1E1, 0xFFFFFF, 0xB4B4B4, "Sound notifications:", config.soundNotifications))
backgroundLayout:addChild(GUI.button(1, 1, 36, 3, 0xC3C3C3, 0xFFFFFF, 0x969696, 0xFFFFFF, "Save")).onTouch = function()
config.historyLimit = math.floor(slider.value)
config.soundNotifications = switchAndLabel.switch.state
backgroundContainer.hidden = true
workspace:draw()
saveConfig()
end
workspace:draw()
end
contactAddButton.onTouch = function()
backgroundContainer.hidden = false
backgroundLayout:removeChildren()
local input = backgroundLayout:addChild(GUI.input(1, 1, 36, 3, 0xE1E1E1, 0x787878, 0xB4B4B4, 0xE1E1E1, 0x2D2D2D, "", "User or chat name"))
backgroundLayout:addChild(GUI.button(1, 1, 36, 3, 0xB4B4B4, 0xFFFFFF, 0x969696, 0xB4B4B4, "Add contact")).onTouch = function()
backgroundContainer.hidden = true
if #input.text > 0 and not history[input.text] then
selectItem(addContactItemToList(input.text))
else
workspace:draw()
end
end
workspace:draw()
end
contactRemoveButton.onTouch = function()
history[selectedItem.text] = nil
selectedItem:remove()
if selectedItem.joined then
socketWrite("PART " .. selectedItem.text)
end
selectItem(systemList:getItem(socketUsername))
updateLeftLayout()
workspace:draw()
end
loginPasswordSwitchAndLabel.switch.onStateChanged = function()
status()
end
window.onResize = function(width, height)
backgroundContainer.width, backgroundContainer.height = width, height
backgroundPanel.width, backgroundPanel.height = backgroundContainer.width, backgroundContainer.height
backgroundLayout.width, backgroundLayout.height = backgroundContainer.width, backgroundContainer.height
chat.localX, chat.width, chat.height = leftLayout.width + 1, width - leftLayout.width - window.backgroundPanel.width, height - chatInput.height
window.backgroundPanel.localX, window.backgroundPanel.height = width - window.backgroundPanel.width + 1, height
rightLayout.localX, rightLayout.height = window.backgroundPanel.localX + 1, window.backgroundPanel.height
leftPanel.height = height - contactLayout.height
leftLayout.height = leftPanel.height
contactLayout.localY = height - contactLayout.height + 1
scrollBar.localX, scrollBar.height = chat.localX + chat.width - 1, chat.height
chatInputLayout.localX, chatInputLayout.localY, chatInputLayout.width = chat.localX, height - chatInputLayout.height + 1, chat.width
chatInput.width = chatInputLayout.width - chatComboBox.width
end
scrollBar.onTouch = function()
scrollBar.value = number.round(scrollBar.value)
if scrollBar.value < 1 then
scrollBar.value = 1
elseif scrollBar.value > #history[selectedItem.text] then
scrollBar.value = #history[selectedItem.text]
end
workspace:draw()
end
local overrideWindowRemove = window.remove
window.remove = function(...)
if socketHandle then
socketWrite("QUIT")
end
overrideWindowRemove(...)
for key in pairs(history) do
local i = 1
while i <= #history[key] do
if history[key][i].text then
history[key][i].text = history[key][i].text:gsub("\\", "\\\\")
history[key][i].text = history[key][i].text:gsub("\"", "\\\"")
history[key][i].text = history[key][i].text:gsub("\'", "\\\'")
end
if history[key][i].newMessages then
table.remove(history[key], i)
else
i = i + 1
end
end
if #history[key] > 0 then
table.insert(history[key], {newMessages = true})
end
end
filesystem.writeTable(historyPath, history)
end
-------------------------------------------------------------------------------
for key in pairs(history) do
addContactItemToList(key)
end
window:resize(window.width, window.height)
selectItem(systemList:getItem(socketUsername))
login()
-- backgroundContainer.hidden = true