version="xsTPTIRC v1.1 beta by mniip"
local n=tpt.get_name()
if not n or n=="" then
error"you MUST be logged in to use xsTPTIrc"
end
local mynick=n
local conf={}
local function loadpresets()
local f=io.open"irc.conf"
if not f then
f=io.open("irc.conf","w")
f:write('nick="'..mynick..[["
server="irc.freenode.net"
port=6667
autojoin={"#powder-social"}
wcolor={255,255,0}
tabtextcolor={255,255,255}
ctabcolor={255,0,255,63}
nickcolor={0,255,255}
textcolor={127,255,255}
opcolor={255,0,0}
voicecolor={64,192,64}
mycolor={255,255,255}
mecolor={128,128,128}
chancolor={255,127,0}
]])
f:close()
f=io.open"irc.conf"
end
local s=f:read"*a"
f:close()
f=loadstring(s)
if f then
setfenv(f,conf)
f()
end
mynick=tostring(conf.nick) or n
conf.server=tostring(conf.server) or "irc.freenode.net"
conf.port=tonumber(conf.port) or 6667
conf.autojoin=type(conf.autojoin)=="table" and conf.autojoin or {"#powder"}
end
loadpresets()
pcall(package.loadlib,"socket","luasocket.dll")
local loader=package.loadlib("luasocket.dll","luaopen_socket_core")
if loader then
loader()
else
socket=require("socket")
end
local drawtext=function(x,y,s,...)return pcall(tpt.drawtext,x,y,tostring(s):gsub("\15(.)(.)(.)",
function(r,g,b)return "\15"..(r=="\0"and"\1"or r)..(g=="\0"and"\1"or g)..(b=="\0"and"\1"or b)
end),...)end
local function textwidth(s)
return select(2,pcall(tpt.textwidth,s))
end
local function r(t)
return "\15"..string.char(unpack(t))
end
local tabs
local ctab=1
local edit=""
local cpos=0
local cursorbl=0
local history={[0]=""}
local histp=0
local wx,wy=10,10
local wcolor=conf.wcolor or {255,255,0}
local tabtextcolor=conf.tabtextcolor or {255,255,255}
local ctabcolor=conf.ctabcolor or {255,0,255,63}
local nickcolor=conf.nickcolor or {0,255,255}
local textcolor=conf.textcolor or {127,255,255}
local opcolor=conf.opcolor or {255,0,0}
local voicecolor=conf.voicecolor or {64,192,64}
local mycolor=conf.mycolor or {255,255,255}
local mecolor=conf.mecolor or {128,128,128}
local chancolor=conf.chancolor or {255,127,0}
local function ui()
tpt.fillrect(wx,wy,600,360,0,0,0,240)
-- Window
tpt.drawrect(wx,wy,600,360,unpack(wcolor))
-- Topic
tpt.drawrect(wx,wy+12,484,12,unpack(wcolor))
local f=#tabs[ctab].topic
while textwidth(tabs[ctab].topic:sub(1,f))>480 do
f=f-1
end
drawtext(wx+2,wy+14,tabs[ctab].topic:sub(1,f),unpack(wcolor))
tpt.drawrect(wx+484,wy+12,12,12,unpack(wcolor))
-- Tabs
if ctab>#tabs then
ctab=#tabs
end
local ox=0
for i=1,#tabs do
tpt.drawrect(wx+ox,wy,#tabs[i].name*6+2,12,unpack(wcolor))
drawtext(wx+ox+2,wy+2,tabs[i].name,unpack(tabtextcolor))
if i==ctab then
tpt.fillrect(wx+ox,wy,#tabs[i].name*6+3,12,unpack(ctabcolor))
end
ox=ox+#tabs[i].name*6+2
end
-- Nicklist
tpt.drawrect(wx+496,wy+12,104,336,unpack(wcolor))
for i,v in ipairs(tabs[ctab].nicks) do
if i<=33 then
if v:gsub("[^@+]","")=="@" then
drawtext(wx+498,wy+4+10*i,v,unpack(opcolor))
elseif v:gsub("[^@+]","")=="+" then
drawtext(wx+498,wy+4+10*i,v,unpack(voicecolor))
else
drawtext(wx+498,wy+4+10*i,v,unpack(nickcolor))
end
end
end
tpt.drawrect(wx,wy+24,496,324,unpack(wcolor))
tpt.drawrect(wx+587,wy+348,13,12,unpack(wcolor))
drawtext(wx+589,wy+351,">>",unpack(wcolor))
-- Text
for i=1,math.min(#tabs[ctab].text,32) do
drawtext(wx+2,wy+348-i*10,r(textcolor)..tabs[ctab].text[i])
end
drawtext(wx+2,wy+351,edit,unpack(textcolor))
if math.floor(cursorbl/10)%2==1 then
tpt.fillrect(wx+2+textwidth(edit:sub(1,cpos)),wy+354,textwidth((edit:sub(cpos+1,cpos+1).." "):sub(1,1))+2,4,unpack(textcolor))
end
cursorbl=cursorbl+1
end
local function isinrect(x,y,xz,yz,w,h)
return x>=xz and y>=yz and x<xz+w and y<yz+h
end
local function wprint(n,s)
local t={[0]="\1\1\1","\255\255\255","\1\1\128","\1\128\1","\255\1\1","\128\1\1","\128\1\128","\255\128\1","\255\255\1","\1\255\1","\1\128\128","\1\255\255"}
s=s:gsub("\3(%d%d?)",function(i)return t[tonumber(i)]and"\15"..t[tonumber(i)]or""end)
while #s>0 do
print(s)
local f=#s
while textwidth(s:sub(1,f))>493 do
f=f-1
end
table.insert(tabs[n].text,1,s:sub(1,f))
s=s:sub(f+1)
end
end
local function findtab(s)
for i,v in ipairs(tabs) do
if s:lower()==v.name:lower() then
return i
end
end
end
local function resetwindows()
tabs={{name="-server-",
nicks={},
topic=version,
text={"--- "..version.." ---","The idea becomes real","Line by line"}}}
ctab=1
edit=""
cpos=0
end
local function send()
table.insert(history,1,edit)
cpos=0
if edit:match"^/" then
local command,params=edit:match"/(%S+)%s*(.*)"
if command:lower()=="clear" then
if params~="" then
(tabs[findtab(params)or tonumber(params)or -1]or {}).text={}
else
tabs[ctab].text={}
end
elseif command:lower()=="close" or command:lower()=="part" then
if params~="" then
table.remove(tabs,findtab(params)or tonumber(params) or 0)
else
table.remove(tabs,ctab)
end
elseif command:lower()=="open" or command:lower()=="query" then
table.insert(tabs,{name=params,nicks={},topic="",text={}})
elseif command:lower()=="ctcp" then
local who,what=params:match"(%S+)%s*(.*)"
c:send("PRIVMSG "..who.." :\1"..what.."\1\n")
elseif command:lower()=="me" then
c:send("PRIVMSG "..tabs[ctab].name.." :\1ACTION "..params.."\1\n")
wprint(ctab,r(mecolor).."* "..mynick.." "..params)
elseif command:lower()=="server" then
if c then
pcall(c.send,c,"QUIT\n")
pcall(c.close,c)
c=nil
end
resetwindows()
c=socket.tcp()
if params:find"%S" then
local server,port=params:match"(%S+)%s+(%S+)"
server=server or params
port=port or 6667
c:connect(server,tonumber(port))
else
c:connect(conf.server,conf.port)
end
c:settimeout(0)
else
c:send(edit:match"/(.*)".."\n")
wprint(ctab,">"..edit)
end
else
c:send("PRIVMSG "..tabs[ctab].name.." :"..edit.."\n")
wprint(ctab,r(mycolor).."<"..mynick.."> "..edit)
end
edit=""
end
local function mouse(x,y,b,n)
if n==1 then
-- Tabs
local ox=0
for i=1,#tabs do
if isinrect(x,y,wx+ox,wy,#tabs[i].name*6+3,12) then
ctab=i
end
ox=ox+#tabs[i].name*6+2
end
if isinrect(x,y,wx+587,wy+348,13,12) then
send()
end
end
return false
end
local function resortnicks(n)
local d=tabs[n].nicks
local a,b={},{}
for _,v in ipairs(d) do
a[#a+1],b[#b+1]=v:match"([@+]?)(.*)"
end
local op,vc,rg={},{},{}
for i,v in ipairs(a) do
if v=="@" then
table.insert(op,b[i])
elseif v=="+" then
table.insert(vc,b[i])
else
table.insert(rg,b[i])
end
end
local function f(a,b)return a:lower()<b:lower() end
table.sort(op,f)
table.sort(vc,f)
table.sort(rg,f)
for i,_ in ipairs(d) do
d[i]=nil
end
for _,v in ipairs(op) do
table.insert(d,"@"..v)
end
for _,v in ipairs(vc) do
table.insert(d,"+"..v)
end
for _,v in ipairs(rg) do
table.insert(d,v)
end
end
local function receive()
local s,e=c:receive"*l"
if s then
s=s:gsub("\n","")
local sender,command,params=s:match":(%S+) (%S+)%s*(.*)"
if not sender then
command,params=s:match"(%S+)%s*(.*)"
end
if s:match"(.-)NOTICE %* :%*%*%* Checking Ident" then
c:send("NICK "..mynick.."\n")
c:send("USER xstptirc _ _ :TPT login:"..n.."\n")
elseif command:lower()=="join" then
local channel=params:match"(%S+)"
local nick=sender:match"([^!]+)"
if nick:lower()==mynick:lower() then
table.insert(tabs,{name=channel,nicks={},topic="",text={}})
ctab=#tabs
wprint(1,r(chancolor).."*** You have joined "..channel)
else
table.insert(tabs[findtab(channel)or 1].nicks,nick)
end
wprint(findtab(channel)or 1,r(chancolor).."*** "..nick.." has joined "..channel)
resortnicks(findtab(channel)or 1)
elseif command=="376" then
for _,v in ipairs(conf.autojoin) do
c:send("JOIN "..v.."\n")
end
elseif command:lower()=="privmsg" then
local channel,text=params:match"(%S+) :(.*)"
local nick=sender:match"([^!]+)"
if text:find"\1" then
local command,params=text:match"\1(%S+)%s*(.*)\1"
if command:lower()=="action" then
if channel:lower()==mynick:lower() then
if not findtab(nick) then
table.insert(tabs,{name=nick,nicks={},topic="Query with "..nick,text={}})
end
channel=nick
end
wprint(findtab(channel)or 1,"* "..nick.." "..params)
else
local ctcp={ping=function(p)return p end,time=function(p)return os.date"%c"end,version=function(p)return version end,userinfo=function(p)return "PING TIME VERSION USERINFO"end}
if ctcp[command:lower()] then
c:send("NOTICE "..nick.." :\1"..command.." "..ctcp[command:lower()](params).."\1\n")
end
end
else
if channel:lower()==mynick:lower() then
if not findtab(nick) then
table.insert(tabs,{name=nick,nicks={},topic="Query with "..nick,text={}})
end
channel=nick
end
for _,v in ipairs(tabs[ctab].nicks) do
if v:gsub("[@+]",""):lower()==nick:lower() then
if v:gsub("[^@+]","")=="@" then
nick=r(opcolor)..nick..r(textcolor)
elseif v:gsub("[^@+]","")=="+" then
nick=r(voicecolor)..nick..r(textcolor)
end
end
end
wprint(findtab(channel)or 1,"<"..nick.."> "..text)
end
elseif command=="433" then
mynick=mynick.."-"
c:send("NICK "..mynick.."\n")
elseif command=="332" then
local channel,topic=params:match"%S+ (%S+) :(.*)"
tabs[findtab(channel)or 1].topic=topic
wprint(findtab(channel)or 1,r(chancolor).."*** Topic for "..channel.." is: ")
wprint(findtab(channel)or 1,r(chancolor)..topic)
elseif command=="333" then
local channel,whom,when=params:match"%S+ (%S+) (%S+) (%S+)"
wprint(findtab(channel)or 1,r(chancolor).."*** Topic set by "..whom.." at "..os.date("%c",when))
elseif command=="353" then
local channel,nicks=params:match"%S+ = (%S+) :(.*)"
for nick in nicks:gmatch"(%S+)" do
table.insert(tabs[findtab(channel)or 0].nicks,nick)
end
resortnicks(findtab(channel)or 0)
elseif command=="366" then
elseif command:lower()=="ping" then
c:send("PONG\n")
elseif command:lower()=="part" then
local channel=params:match"(%S+)"
local nick=sender:match"([^!]+)"
for i,v in ipairs(tabs[findtab(channel)or 0].nicks) do
if v:lower():gsub("[@+]","")==nick:lower() then
table.remove(tabs[findtab(channel)or 1].nicks,i)
break
end
end
wprint(findtab(channel)or 1,r(chancolor).."*** "..nick.." has left "..channel)
if nick:lower()==mynick:lower() then
table.remove(tabs,findtab(channel)or -1)
wprint(1,r(chancolor).."*** You have left "..channel)
end
elseif command:lower()=="quit" then
local nick=sender:match"([^!]+)"
local reason=params:match"%s*:(.*)"
for i,v in ipairs(tabs) do
for j,n in ipairs(v.nicks) do
if n:lower():gsub("[@+]","")==nick:lower() then
wprint(i,r(chancolor).."*** "..nick.." has quit ("..reason..")")
table.remove(v.nicks,j)
break
end
end
end
elseif command:lower()=="mode" then
local nick=sender:match"([^!]+)"
local whom,modes=params:match"(%S+) (.*)"
wprint(findtab(whom)or 1,r(chancolor).."*** "..nick.." sets modes for "..whom.." : "..modes)
if whom:match"#" then
tabs[findtab(whom)or 1].nicks={}
c:send("NAMES "..whom.."\n")
end
elseif command:lower()=="nick" then
local nick=sender:match"([^!]+)"
local newnick=params:match":(%S+)"
for i,v in ipairs(tabs) do
for j,n in ipairs(v.nicks) do
if n:lower():gsub("[@+]","")==nick:lower() then
wprint(i,r(chancolor).."*** "..nick.." has changed nick to "..newnick)
v.nicks[j]=n:gsub("[^@+]","")..newnick
break
end
end
end
if nick:lower()==mynick:lower() then
mynick=newnick
end
elseif command:lower()=="notice" then
local nick=sender:match"([^!]+)"
local text=params:match"%S+ :(.*)"
wprint(ctab,"-- "..nick.." -- "..text)
else
wprint(1,s)
end
else
if e~="timeout" then
wprint(ctab,"*** ERROR READING FROM SOCKET: "..e)
c=nil
end
end
end
local function caps(a)
local from="`1234567890-=qwertyuiop[]\\asdfghjkl;'zxcvbnm,./"
local to= "~!@#$%^&*()_+QWERTYUIOP{}|ASDFGHJKL:\"ZXCVBNM<>?"
local s,e=from:find(a,1,true)
return to:sub(s,e) or a
end
local function key(a,b,c,d)
if d==1 then
if b>31 and b<127 then
edit=edit:sub(1,cpos)..(c%4==0 and string.char(b) or caps(string.char(b)))..edit:sub(cpos+1,#edit)
cpos=cpos+1
elseif b==13 then
send()
elseif b==8 then
edit=edit:sub(1,math.max(cpos-1,0))..edit:sub(cpos+1,#edit)
cpos=cpos-1
if cpos<0 then
cpos=0
end
elseif b==127 then
edit=edit:sub(1,cpos)..edit:sub(cpos+2,#edit)
elseif b==9 then
local txt,lw=edit:match"(.*%s)(%S+)"
lw=lw or edit
txt=txt or ""
for _,v in ipairs(tabs[ctab].nicks) do
if v:gsub("[@+]",""):sub(1,#lw):lower()==lw:lower() then
edit=txt..v:gsub("[@+]","")
end
end
elseif b==276 then
cpos=cpos-1
if cpos<0 then
cpos=0
end
elseif b==275 then
cpos=cpos+1
if cpos>#edit then
cpos=#edit
end
elseif b==274 then
histp=histp-1
if cpos<0 then
cpos=0
end
edit=history[histp]
cpos=#edit
elseif b==273 then
histp=histp+1
if histp>#history then
histp=#history
end
edit=history[histp]
cpos=#edit
else
return true
end
end
return false
end
local launched=false
local visible=false
local function appear()
if not visible then
tpt.register_step(ui)
tpt.register_mouseclick(mouse)
tpt.register_keypress(key)
end
visible=true
end
local function disappear()
if visible then
tpt.unregister_step(ui)
tpt.unregister_mouseclick(mouse)
tpt.unregister_keypress(key)
end
visible=false
end
local function start()
if not launched then
resetwindows()
c=socket.tcp()
c:connect(conf.server,conf.port)
c:settimeout(0)
tpt.register_step(receive)
end
launched=true
end
local function stop()
if launched then
c:send"QUIT\n"
c:close()
c=nil
tpt.unregister_step(receive)
end
disappear()
launched=false
end
local function keyhandler(a,b,c,d)
if d==1 and (math.floor(c/64)%2==1 or math.floor(c/128)%2==1) then
if a=="m" then
if c%4==0 then
if visible then
disappear()
else
if not launched then
start()
end
appear()
end
else
disappear()
stop()
end
return false
end
end
end
tpt.register_keypress(keyhandler)