commit all archived files
This commit is contained in:
parent
ee213d434b
commit
2b7b91ccc2
25
README.md
Normal file
25
README.md
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# mcompletescript
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
A PtokaX extension adding several features.
|
||||||
|
|
||||||
|
User features:
|
||||||
|
- Catchup chat history
|
||||||
|
- Full chat archive
|
||||||
|
- User registration and password change support
|
||||||
|
- Block search terms
|
||||||
|
- Block invisible space characters in user nicks
|
||||||
|
- Group chat support with UserCommand integration
|
||||||
|
|
||||||
|
Administrator features:
|
||||||
|
- Manage ptokax scripts
|
||||||
|
- Retrieve hub statistics
|
||||||
|
- Impersonate users
|
||||||
|
|
||||||
|
Tags: nmdc
|
||||||
|
|
||||||
|
|
||||||
|
## Download
|
||||||
|
|
||||||
|
- [⬇️ mCompleteScript_v20.lua](dist-archive/mCompleteScript_v20.lua) *(21.02 KiB)*
|
699
dist-archive/mCompleteScript_v20.lua
Normal file
699
dist-archive/mCompleteScript_v20.lua
Normal file
@ -0,0 +1,699 @@
|
|||||||
|
--[[ _ _ /~` _ _ _ _ | _ _|_ _ (~ _ _. _ _|_
|
||||||
|
| | |\_,(_)| | ||_)|(/_ | (/__)(_| ||_) | v21
|
||||||
|
| |
|
||||||
|
History: Mappy v1..17 2008-2009, v18 Jan 2010, v19 Dec 2012, v20 Jan 2013, v21 Aug 2013
|
||||||
|
For time/date formatting, see lua.org/pil/22.1.html
|
||||||
|
]]
|
||||||
|
|
||||||
|
-----------------------------------------------------------[ options ]
|
||||||
|
|
||||||
|
-- Configure features
|
||||||
|
bBlockInvisibleCharacters = false
|
||||||
|
bBlockIllegalSearches = false
|
||||||
|
bEnableCatchup = true
|
||||||
|
bEnableRegistration = true
|
||||||
|
bEnableUserLocations = false
|
||||||
|
bEnableImpersonate = false
|
||||||
|
bEnableChatLogging = true
|
||||||
|
bEnableGroupChat = true
|
||||||
|
|
||||||
|
il = 15 -- Number of chat log messages to keep in memory
|
||||||
|
tf = "[%H.%M]" -- Format of chat log timestamps
|
||||||
|
AdminLevel = 0 -- Permission required for admin features (by default 0 is 'Master')
|
||||||
|
RegLevel = 3 -- Permission given upon registration (by default 3 is 'Registered')
|
||||||
|
PLL = 5 -- Minimum password length (must be at least one)
|
||||||
|
|
||||||
|
-- Words that flag searches;
|
||||||
|
badwords = {"clop"}
|
||||||
|
|
||||||
|
-- Settings for chat logs
|
||||||
|
chatLogPath = Core.GetPtokaXPath().."logs/%B - %Y - 127.0.0.1.txt"
|
||||||
|
chatLogTimeFormat = "%Y-%m-%d %H:%M:%S"
|
||||||
|
|
||||||
|
-- Settings for group chats
|
||||||
|
groupChatPruneCheckEvery = 30
|
||||||
|
groupChatTimeoutAfter = 15*60
|
||||||
|
|
||||||
|
-- Strings
|
||||||
|
SZ_REG_CONNECTED = "Thank you for using a registered nickname!"
|
||||||
|
SZ_UNREG_CONNECTED = "Want to secure your nickname? Register it."
|
||||||
|
SZ_REG_COMPLETED = "Your nickname is now registered. In future, log on with your password."
|
||||||
|
SZ_INVISIBLE_CHAR = "Sorry, remove all invisible characters from your name to reconnect."
|
||||||
|
SZ_REDIR_REASON = "Rearranging network, please be patient..."
|
||||||
|
SZ_E_DISABLED = "* Feature disabled."
|
||||||
|
SZ_E_SYNTAX_ERROR = "* Syntax error."
|
||||||
|
SZ_E_UNAUTHORISED = "* Unauthorised."
|
||||||
|
SZ_E_SCRIPT_ERROR = "* Error performing script operation."
|
||||||
|
|
||||||
|
SZ_GCHAT_CLOSING = "## Closing group chat..."
|
||||||
|
SZ_GCHAT_AWAYMSG = "Away"
|
||||||
|
|
||||||
|
-----------------------------------------------------------[ data storage ]
|
||||||
|
|
||||||
|
groupchats = {}
|
||||||
|
groupChatTimer = nil
|
||||||
|
ml = {""}
|
||||||
|
mp = -1
|
||||||
|
hall = {
|
||||||
|
-- 1-indexed,
|
||||||
|
"Rochester and Rutherford", -- 8..15
|
||||||
|
"Ilam Apartments", -- 16..31
|
||||||
|
"Ilam Apartments",
|
||||||
|
"Bishop Julius", -- 32..39
|
||||||
|
"College House", -- 40..47
|
||||||
|
"University Hall", -- 48..55
|
||||||
|
"Sonada" -- 56..63
|
||||||
|
}
|
||||||
|
-----------------------------------------------------------[ connections ]
|
||||||
|
|
||||||
|
function OpConnected(user) SC(user) end
|
||||||
|
function RegConnected(user) SC(user) end
|
||||||
|
|
||||||
|
function UserConnected(user)
|
||||||
|
if bBlockInvisibleCharacters then
|
||||||
|
-- Only filters unregistered users
|
||||||
|
if string.find(user.sNick, "") then
|
||||||
|
Core.SendToUser(user, "* "..SZ_INVISIBLE_CHAR)
|
||||||
|
BanMan.TempBanNick(user.sNick, 1, "Use of invisible character", Core.GetHubSecAlias)
|
||||||
|
Core.SendToOps("<"..Core.GetHubSecAlias.."> "..user.sNick.." tried to use an invisible character!")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
SC(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
function SC(user, sendMessages)
|
||||||
|
-- CONTEXT_HUB = 0x01
|
||||||
|
-- CONTEXT_USER = 0x02
|
||||||
|
if sendMessages == nil then sendMessages = true end
|
||||||
|
if not user then return end
|
||||||
|
|
||||||
|
-- Clear existing
|
||||||
|
AddUSC(user, "255 3", "", "")
|
||||||
|
|
||||||
|
-- Locations
|
||||||
|
if (bEnableUserLocations) then
|
||||||
|
AddUSC(user, "1 2", "Get User Location", "!locate %[nick]")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Registration
|
||||||
|
if (bEnableRegistration) then
|
||||||
|
if user.iProfile == RegLevel then
|
||||||
|
if sendMessages then Core.SendToUser(user, "* "..SZ_REG_CONNECTED) end
|
||||||
|
AddUSC(user, "1 1", "Change your password...", "!reg %[line:Enter a password]")
|
||||||
|
else
|
||||||
|
if sendMessages then Core.SendToUser(user, "* "..SZ_UNREG_CONNECTED) end
|
||||||
|
AddUSC(user, "1 1", "Register your nick...", "!reg %[line:Enter a password]")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Catchup
|
||||||
|
if (bEnableCatchup) then
|
||||||
|
if sendMessages then SendCatchup(user) end
|
||||||
|
AddUSC(user, "1 1", "Get chat history", "!catchup")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Impersonation
|
||||||
|
if (bEnableImpersonate or user.iProfile == AdminLevel) then
|
||||||
|
AddUSC(user, "1 2", "Impersonate\\Say as user...", "!yuo %[nick] %[line:Enter text]")
|
||||||
|
AddUSC(user, "1 2", "Impersonate\\Action as user [normal]...", "!yuo %[nick] /me %[line:Enter text]")
|
||||||
|
AddUSC(user, "1 2", "Impersonate\\Action as user [system]...", "!you %[nick] %[line:Enter text]")
|
||||||
|
AddUSC(user, "0 2", "", "")
|
||||||
|
AddUSC(user, "1 3", "Impersonate\\Send system message...", "!you %[line:Enter text]")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Group chats
|
||||||
|
if (bEnableGroupChat) then
|
||||||
|
AddUSC(user, "1 1", "Group chat\\New group chat", "!groupchat_new")
|
||||||
|
local foundAnyChats = false
|
||||||
|
for botnick, grinfo in pairs(groupchats) do
|
||||||
|
if grinfo["owner"] == user.sNick then
|
||||||
|
if not foundAnyChats then
|
||||||
|
AddUSC(user, "1 1", "Group chat\\-", "")
|
||||||
|
foundAnyChats = true
|
||||||
|
end
|
||||||
|
AddUSC(user, "1 1", "Group chat\\Close chat ["..botnick.."]", "!groupchat_close "..botnick)
|
||||||
|
AddUSC(user, "1 2", "Invite user to join ["..botnick.."]", "!groupchat_invite "..botnick.." %[nick]")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Admin tasks
|
||||||
|
if (user.iProfile == AdminLevel) then
|
||||||
|
|
||||||
|
AddUSC(user, "1 1", "Admin\\Restart PtokaX", "!restart")
|
||||||
|
AddUSC(user, "1 1", "Admin\\Redirect all users...", "!redirectallusers %[line:Redirect address]")
|
||||||
|
AddUSC(user, "1 1", "Admin\\Set hub title...", "!sethubtitle %[line:Hub title]")
|
||||||
|
AddUSC(user, "1 1", "Admin\\Reload MOTD from disk", "!reload-motd")
|
||||||
|
AddUSC(user, "0 1", "Admin\\-", "")
|
||||||
|
AddUSC(user, "1 2", "Admin\\Look up user password", "!get-password %[nick]")
|
||||||
|
if bEnableCatchup then
|
||||||
|
AddUSC(user, "1 1", "Admin\\Flush catchup ring buffer", "!catchup-flush")
|
||||||
|
end
|
||||||
|
AddUSC(user, "1 1", "Admin\\Chat as "..Core.GetHubSecAlias().."...", "!yuo "..Core.GetHubSecAlias().." %[line:Enter text]")
|
||||||
|
AddUSC(user, "1 1", "Admin\\Get hub stats", "!get-stats")
|
||||||
|
AddUSC(user, "1 1", "Admin\\Refresh menu", "!refreshmenu")
|
||||||
|
|
||||||
|
-- Scripts menu
|
||||||
|
|
||||||
|
AddUSC(user, "0 1", "Admin\\-", "")
|
||||||
|
ScriptMan.Refresh()
|
||||||
|
for i,v in ipairs(ScriptMan.GetScripts()) do
|
||||||
|
local title = "Admin\\Scripts\\["..(v.bEnabled and "X" or " ").."] "..v.sName.."\\"
|
||||||
|
if (v.bEnabled) then
|
||||||
|
AddUSC(user, "1 1", title.."Disable", "!stop-script "..v.sName)
|
||||||
|
else
|
||||||
|
AddUSC(user, "1 1", title.."Enable", "!start-script "..v.sName)
|
||||||
|
end
|
||||||
|
AddUSC(user, "1 1", title.."Restart", "!restart-script "..v.sName)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-----------------------------------------------------------[ main parsing ]
|
||||||
|
|
||||||
|
triggers = {
|
||||||
|
|
||||||
|
-- Usage: !locate username
|
||||||
|
["!locate"] = function(user, data, t)
|
||||||
|
if not bEnableUserLocations then return Core.SendToUser(user, SZ_E_DISABLED) end
|
||||||
|
if #t ~= 2 then return Core.SendToUser(user, SZ_E_SYNTAX_ERROR) end
|
||||||
|
|
||||||
|
Core.SendToUser(user, GetHallFromIP(t[2]))
|
||||||
|
end,
|
||||||
|
|
||||||
|
-- Usage: !reg password
|
||||||
|
["!reg"] = function(user, data, t)
|
||||||
|
if not bEnableRegistration then return Core.SendToUser(user, SZ_E_DISABLED) end
|
||||||
|
if #t ~= 2 then return Core.SendToUser(user, SZ_E_SYNTAX_ERROR) end
|
||||||
|
|
||||||
|
if string.len(t[2])>=PLL then
|
||||||
|
if RegMan.GetReg(user.sNick)==nil then
|
||||||
|
RegMan.AddReg(user.sNick, t[2], RegLevel)
|
||||||
|
Core.SendToUser(user, "* "..SZ_REG_COMPLETED)
|
||||||
|
else
|
||||||
|
RegMan.ChangeReg(user.sNick, t[2], RegLevel)
|
||||||
|
Core.SendToUser(user, "* Password changed.")
|
||||||
|
end
|
||||||
|
RegMan.Save()
|
||||||
|
else
|
||||||
|
Core.SendToUser(user, "* Passwords must be at least "..PLL.." characters long.")
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
-- Usage: !catchup
|
||||||
|
["!catchup"] = function(user, data, t)
|
||||||
|
if not bEnableCatchup then return Core.SendToUser(user, SZ_E_DISABLED) end
|
||||||
|
|
||||||
|
SendCatchup(user)
|
||||||
|
end,
|
||||||
|
|
||||||
|
-- Action as user
|
||||||
|
-- Usage: !you their-nick message goes here
|
||||||
|
["!you"] = function(user, data, t)
|
||||||
|
if bEnableImpersonate or user.iProfile == AdminLevel then
|
||||||
|
local m
|
||||||
|
_,m=s2(data)
|
||||||
|
_,m=s2(m)
|
||||||
|
Core.SendToAll("* "..m)
|
||||||
|
logCatchup("* "..m)
|
||||||
|
else
|
||||||
|
Core.SendToUser(user, SZ_E_DISABLED)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
-- Say as user
|
||||||
|
-- Usage: !yuo their-nick message goes here
|
||||||
|
["!yuo"] = function(user, data, t)
|
||||||
|
if bEnableImpersonate or user.iProfile == AdminLevel then
|
||||||
|
local m, u, mg
|
||||||
|
_,m=s2(data)
|
||||||
|
_,m=s2(m)
|
||||||
|
u,mg=s2(m)
|
||||||
|
Core.SendToAll("<"..u.."> "..mg)
|
||||||
|
logCatchup("<"..u.."> "..mg)
|
||||||
|
else
|
||||||
|
Core.SendToUser(user, Z_E_DISABLED)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
-- Initiate a group chat
|
||||||
|
-- Usage: !groupchat_new
|
||||||
|
["!groupchat_new"] = function(user, data, t)
|
||||||
|
if bEnableGroupChat or user.iProfile == AdminLevel then
|
||||||
|
if #t ~= 1 then return Core.SendToUser(user, SZ_E_SYNTAX_ERROR) end
|
||||||
|
groupchat_init(user.sNick)
|
||||||
|
else
|
||||||
|
Core.SendToUser(user, Z_E_DISABLED)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
-- Invite a user to a group chat under your control
|
||||||
|
-- Usage: !groupchat_invite botnick friendnick
|
||||||
|
["!groupchat_invite"] = function(user, data, t)
|
||||||
|
if bEnableGroupChat or user.iProfile == AdminLevel then
|
||||||
|
if #t ~= 3 then return Core.SendToUser(user, SZ_E_SYNTAX_ERROR) end
|
||||||
|
groupchat_invite(user, t[2], t[3])
|
||||||
|
else
|
||||||
|
Core.SendToUser(user, Z_E_DISABLED)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
-- Close a group chat under your control
|
||||||
|
-- Usage: !groupchat_close botnick
|
||||||
|
["!groupchat_close"] = function(user, data, t)
|
||||||
|
if bEnableGroupChat or user.iProfile == AdminLevel then
|
||||||
|
if #t ~= 2 then return Core.SendToUser(user, SZ_E_SYNTAX_ERROR) end
|
||||||
|
groupchat_end(user, t[2])
|
||||||
|
else
|
||||||
|
Core.SendToUser(user, Z_E_DISABLED)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
}
|
||||||
|
adminTriggers = {
|
||||||
|
|
||||||
|
-- Usage: !get-password their-nick
|
||||||
|
["!get-password"] = function(user, data, t)
|
||||||
|
if #t ~= 2 then return Core.SendToUser(user, SZ_E_SYNTAX_ERROR) end
|
||||||
|
|
||||||
|
local reguser = RegMan.GetReg(t[2])
|
||||||
|
if reguser then
|
||||||
|
Core.SendToUser(user, "User "..reguser.sNick.." has password '"..reguser.sPassword.."'")
|
||||||
|
else
|
||||||
|
Core.SendToUser(user, "User "..t[2].." not registered.")
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
-- Usage: !reload-motd
|
||||||
|
["!reload-motd"] = function(user, data, t)
|
||||||
|
local motd = readFile(Core.GetPtokaXPath().."cfg/Motd.txt", "rb")
|
||||||
|
if motd then
|
||||||
|
SetMan.SetMOTD(motd)
|
||||||
|
SetMan.Save()
|
||||||
|
Core.SendToUser(user, "<"..Core.GetHubSecAlias().."> "..SetMan.GetMOTD())
|
||||||
|
else
|
||||||
|
Core.SendToUser(user, "* Error opening '"..Core.GetPtokaXPath().."cfg/Motd.txt'")
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
-- Usage: !redirectallusers target-address
|
||||||
|
["!redirectallusers"] = function(user, data, t)
|
||||||
|
if #t ~= 2 then return Core.SendToUser(user, SZ_E_SYNTAX_ERROR) end
|
||||||
|
|
||||||
|
local allUsers = Core.GetOnlineUsers()
|
||||||
|
for _, u in pairs(allUsers) do
|
||||||
|
Core.SendToUser(user, "* Redirecting ".. u.sNick .." to "..t[2])
|
||||||
|
Core.Redirect(u, t[2], SZ_REDIR_REASON)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
-- Usage: !refreshmenu
|
||||||
|
["!refreshmenu"] = function(user, data, t)
|
||||||
|
SC(user, false)
|
||||||
|
end,
|
||||||
|
|
||||||
|
-- Usage: !start-script filename.lua
|
||||||
|
["!start-script"] = function(user, data, t)
|
||||||
|
if #t ~= 2 then return Core.SendToUser(user, SZ_E_SYNTAX_ERROR) end
|
||||||
|
if (ScriptMan.StartScript(t[2]) == nil) then Core.SendToUser(user, SZ_E_SCRIPT_ERROR) end
|
||||||
|
|
||||||
|
Core.SendToUser(user, "* Script "..t[2].." started.");
|
||||||
|
SetMan.Save()
|
||||||
|
SC(user, false)
|
||||||
|
end,
|
||||||
|
|
||||||
|
-- Usage: !stop-script filename.lua
|
||||||
|
["!stop-script"] = function(user, data, t)
|
||||||
|
if #t ~= 2 then return Core.SendToUser(user, SZ_E_SYNTAX_ERROR) end
|
||||||
|
if (ScriptMan.StopScript(t[2]) == nil) then Core.SendToUser(user, SZ_E_SCRIPT_ERROR) end
|
||||||
|
|
||||||
|
Core.SendToUser(user, "* Script "..t[2].." stopped.");
|
||||||
|
SetMan.Save()
|
||||||
|
SC(user, false)
|
||||||
|
end,
|
||||||
|
|
||||||
|
-- Usage: !restart-script filename.lua
|
||||||
|
["!restart-script"] = function(user, data, t)
|
||||||
|
if #t ~= 2 then return Core.SendToUser(user, SZ_E_SYNTAX_ERROR) end
|
||||||
|
if (ScriptMan.RestartScript(t[2]) == nil) then Core.SendToUser(user, SZ_E_SCRIPT_ERROR) end
|
||||||
|
|
||||||
|
Core.SendToUser(user, "* Script "..t[2].." restarted.");
|
||||||
|
SetMan.Save()
|
||||||
|
SC(user, false)
|
||||||
|
end,
|
||||||
|
|
||||||
|
-- Usage: !get-stats
|
||||||
|
["!get-stats"] = function(user, data, t)
|
||||||
|
Core.SendToUser(user, "* Peak users this boot: "..Core.GetActualUsersPeak())
|
||||||
|
Core.SendToUser(user, "* Peak users all time: "..Core.GetMaxUsersPeak())
|
||||||
|
Core.SendToUser(user, "* Current share: "..Core.GetCurrentSharedSize())
|
||||||
|
Core.SendToUser(user, "* Uptime: "..Core.GetUpTime().." seconds")
|
||||||
|
end,
|
||||||
|
|
||||||
|
-- Usage: !catchup-flush
|
||||||
|
["!catchup-flush"] = function(user, data, t)
|
||||||
|
flushCatchup()
|
||||||
|
Core.SendToUser(user, "* Catchup flushed.")
|
||||||
|
end,
|
||||||
|
|
||||||
|
-- Usage: !sethubtitle title goes here
|
||||||
|
["!sethubtitle"] = function(user, data, t)
|
||||||
|
SetMan.SetString(0, table.concat(t, " ", 2))
|
||||||
|
SetMan.Save()
|
||||||
|
end,
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function ChatArrival(user, data)
|
||||||
|
local validcommand=false
|
||||||
|
|
||||||
|
if (string.sub(data, 1, 1) == "<" ) then
|
||||||
|
data=string.sub(data, 1, string.len(data) -1)
|
||||||
|
t = {}
|
||||||
|
for k in string.gmatch(data, "%s+([^%s]+)") do
|
||||||
|
table.insert(t, k)
|
||||||
|
end
|
||||||
|
|
||||||
|
if (#t>=1 and triggers[t[1]]) then
|
||||||
|
triggers[t[1]](user, data, t)
|
||||||
|
return true
|
||||||
|
|
||||||
|
elseif (#t>=1 and user.iProfile == AdminLevel and adminTriggers[t[1]]) then
|
||||||
|
adminTriggers[t[1]](user, data, t)
|
||||||
|
return true
|
||||||
|
|
||||||
|
elseif t[1] and string.sub(t[1], 1, 1) == "!" then
|
||||||
|
-- Don't add potential system commands to catchup/logs
|
||||||
|
|
||||||
|
else
|
||||||
|
logCatchup(data:sub(1)) --, -2)) --string.len(data)))
|
||||||
|
logChat(nmdc_unescape(data:sub(1))) --, -2)))
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function ToArrival(user, data)
|
||||||
|
-- Parse data and determine whether it's intended for any groupchat bots
|
||||||
|
local recipient = data:gsub("%$To: ([^%s]+) From: [^%s]+ %$<[^>]+> (.*)|", "%1")
|
||||||
|
local body = data:gsub("%$To: ([^%s]+) From: [^%s]+ %$<[^>]+> (.*)|", "%2")
|
||||||
|
|
||||||
|
-- Core.SendToUser(user, nmdc_escape(data))
|
||||||
|
|
||||||
|
-- Core.SendToUser(user, "got recipient ["..recipient.."] and body ["..body.."]")
|
||||||
|
|
||||||
|
if groupchats[recipient] then
|
||||||
|
groupchat_sayas(recipient, user.sNick, body)
|
||||||
|
return true -- abort
|
||||||
|
else
|
||||||
|
return false -- process normally
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function OnStartup()
|
||||||
|
-- Create groupchat pruner
|
||||||
|
groupChatTimer = TmrMan.AddTimer(groupChatPruneCheckEvery * 1000, "groupchat_prune")
|
||||||
|
end
|
||||||
|
|
||||||
|
-----------------------------------------------------------[ impersonation ]
|
||||||
|
|
||||||
|
function s2(inp)
|
||||||
|
sp = string.find(inp, " ")
|
||||||
|
if sp==nil then sp=0 end
|
||||||
|
return string.sub(inp, 1, sp-1), string.sub(inp, sp+1)
|
||||||
|
end
|
||||||
|
|
||||||
|
-----------------------------------------------------------[ group chat ]
|
||||||
|
|
||||||
|
function groupchat_init(ownernick)
|
||||||
|
-- Generate a nick for the bot by adding numbers to the user's nick until
|
||||||
|
-- one is not currently in use
|
||||||
|
local suffix = 1
|
||||||
|
while true do
|
||||||
|
-- Core.GetUser doesn't include script bots
|
||||||
|
if Core.GetUser(ownernick .. suffix) == nil and not groupchats[ownernick..suffix] then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
suffix = suffix + 1
|
||||||
|
if suffix > 100 then
|
||||||
|
Core.SendToNick(ownernick, "Error: Couldn't pick a group chat username")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local botnick = ownernick..suffix
|
||||||
|
|
||||||
|
-- Register bot
|
||||||
|
if not Core.RegBot(botnick, "", "", false) then
|
||||||
|
Core.SendToNick(ownernick, "Error: Couldn't start group chat")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Save all data
|
||||||
|
groupchats[botnick] = {
|
||||||
|
["owner"] = ownernick,
|
||||||
|
["last"] = os.time(),
|
||||||
|
["users"] = {
|
||||||
|
[ownernick] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Refresh usercommands
|
||||||
|
SC( Core.GetUser(ownernick), false )
|
||||||
|
|
||||||
|
groupchat_say(botnick, "## Joins: "..ownernick)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Owner invites a user into an existing group chat
|
||||||
|
function groupchat_invite(user, botnick, friendnick)
|
||||||
|
if groupchats[botnick] and groupchats[botnick]["owner"] == user.sNick then -- only owner can invite
|
||||||
|
if Core.GetUser(friendnick) then -- Check if user's online
|
||||||
|
|
||||||
|
-- Add user and notify participants
|
||||||
|
groupchats[botnick]["users"][friendnick] = true
|
||||||
|
|
||||||
|
-- Notify self of existing participants
|
||||||
|
for peer,_ in pairs(groupchats[botnick]["users"]) do
|
||||||
|
local isHost = ""
|
||||||
|
if (groupchats[botnick]["owner"] == peer) then
|
||||||
|
isHost = " (host)"
|
||||||
|
end
|
||||||
|
if peer ~= friendnick then
|
||||||
|
-- we go last
|
||||||
|
Core.SendPmToNick(friendnick, botnick, "## Joins: "..peer..isHost)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Notify existing participants (and self) of joining
|
||||||
|
groupchat_say(botnick, "## Joins: "..friendnick) -- updates mtime
|
||||||
|
|
||||||
|
else
|
||||||
|
Core.SendToUser(user, "Error: User not online")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
Core.SendToUser(user, SZ_E_UNAUTHORISED) -- nonexistent or not yours
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Pass user as well, so that administrators can prune groupchat bots.
|
||||||
|
function groupchat_end(user, botnick)
|
||||||
|
if groupchats[botnick] then
|
||||||
|
if groupchats[botnick]["owner"] == user.sNick or user.iProfile == AdminLevel then
|
||||||
|
-- End chat
|
||||||
|
groupchat_remove(botnick)
|
||||||
|
else
|
||||||
|
-- Not allowed
|
||||||
|
Core.SendToUser(user, SZ_E_UNAUTHORISED)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Every minute or so, prune group chats that havn't had any replies in a while.
|
||||||
|
function groupchat_prune()
|
||||||
|
if #groupchats then
|
||||||
|
for botnick, gcdata in pairs(groupchats) do
|
||||||
|
if (os.time() - gcdata["last"]) > groupChatTimeoutAfter then
|
||||||
|
groupchat_remove(botnick)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Explicitly remove a group chat. Only called by other groupchat_ helper
|
||||||
|
-- functions
|
||||||
|
function groupchat_remove(botnick)
|
||||||
|
if groupchats[botnick] then
|
||||||
|
-- Send leaving message to any still-connected users
|
||||||
|
groupchat_say(botnick, SZ_GCHAT_CLOSING)
|
||||||
|
|
||||||
|
-- Part the bot
|
||||||
|
Core.UnregBot(botnick)
|
||||||
|
|
||||||
|
local owner = groupchats[botnick]["owner"]
|
||||||
|
|
||||||
|
-- Remove from groupchat registry
|
||||||
|
groupchats[botnick] = nil
|
||||||
|
|
||||||
|
-- Refresh usercommands for the host
|
||||||
|
SC( Core.GetUser(owner), false )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Explicitly say a message into a group chat. Only called by other groupchat_
|
||||||
|
-- helper functions
|
||||||
|
function groupchat_say(botnick, message)
|
||||||
|
if groupchats[botnick] then
|
||||||
|
for destnick, _ in pairs(groupchats[botnick]["users"]) do
|
||||||
|
Core.SendPmToNick( destnick, botnick, message )
|
||||||
|
end
|
||||||
|
groupchats[botnick]["last"] = os.time()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Say a message from a user (i.e. don't echo it back to them)
|
||||||
|
function groupchat_sayas(botnick, selfnick, message)
|
||||||
|
if groupchats[botnick] then
|
||||||
|
local valid = false;
|
||||||
|
for destnick, _ in pairs(groupchats[botnick]["users"]) do
|
||||||
|
if destnick == selfnick then
|
||||||
|
valid = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if valid then
|
||||||
|
for destnick, _ in pairs(groupchats[botnick]["users"]) do
|
||||||
|
if destnick ~= selfnick then
|
||||||
|
Core.SendPmToNick(destnick, botnick, "<"..selfnick.."> "..message)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
Core.SendPmToNick(selfnick, botnick, SZ_GCHAT_AWAYMSG) -- pretend we're not here
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-----------------------------------------------------------[ search parsing ]
|
||||||
|
|
||||||
|
function SearchArrival(user, data)
|
||||||
|
if bBlockIllegalSearches then
|
||||||
|
sstr=data
|
||||||
|
while string.find(sstr, "?") do
|
||||||
|
sstr=string.sub(sstr, string.find(sstr,"?")+1)
|
||||||
|
end
|
||||||
|
sstr="$"..string.lower( string.sub(sstr,1,string.len(sstr)-1) )
|
||||||
|
|
||||||
|
t = {}
|
||||||
|
for k in string.gmatch(sstr, "%$([^%$]+)") do
|
||||||
|
table.insert(t, k)
|
||||||
|
end
|
||||||
|
sstr=table.concat(t, " ")
|
||||||
|
|
||||||
|
if string.sub(sstr, 1, 4) ~= "tth:" then
|
||||||
|
local showbanner=false
|
||||||
|
for cnt=1,#badwords do
|
||||||
|
if string.find(sstr, badwords[cnt]) then
|
||||||
|
showbanner=true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if showbanner then
|
||||||
|
Core.SendToAll("<"..Core.GetHubSecAlias().."> "..user.sNick.." just searched for '"..sstr.."'!")
|
||||||
|
return true -- disable the search
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-----------------------------------------------------------[ location ]
|
||||||
|
|
||||||
|
function GetHallFromIP(userNick)
|
||||||
|
if Core.GetUser(userNick) == nil then
|
||||||
|
return "* User "..userNick.." doesn't have a location."
|
||||||
|
else
|
||||||
|
-- Dumb check of third digit in IP
|
||||||
|
subnet = string.gsub(Core.GetUser(userNick).sIP, "%d+%D%d+%D(%d+)%D%d+", "%1")
|
||||||
|
subnet = math.floor(subnet / 8)
|
||||||
|
if hall[subnet]==nil then
|
||||||
|
return "* User "..userNick.." is located somewhere unknown"
|
||||||
|
else
|
||||||
|
return "* User "..userNick.." is located at "..hall[subnet]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-----------------------------------------------------------[ catchup ]
|
||||||
|
|
||||||
|
function SendCatchup(user)
|
||||||
|
local sz = getRange(ml, isz(mp), il)..getRange(ml, 0, isz(mp))
|
||||||
|
Core.SendToNick(user.sNick, "* Showing up to "..il.." messages.."..sz)
|
||||||
|
Core.SendToNick(user.sNick, "* Catchup completed.\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
function isz(n) return (n + 1 + il) % il end
|
||||||
|
|
||||||
|
function logCatchup(text)
|
||||||
|
mp = isz(mp)
|
||||||
|
ml[mp] = os.date(tf).." "..text
|
||||||
|
end
|
||||||
|
|
||||||
|
function flushCatchup()
|
||||||
|
ml = {""}
|
||||||
|
mp = -1
|
||||||
|
end
|
||||||
|
|
||||||
|
-----------------------------------------------------------[ chatlogs ]
|
||||||
|
|
||||||
|
function logChat(text)
|
||||||
|
local logfile = os.date(chatLogPath)
|
||||||
|
local f = io.open(logfile, "a+")
|
||||||
|
f:write("["..os.date(chatLogTimeFormat).."] "..text.."\n")
|
||||||
|
f:close()
|
||||||
|
end
|
||||||
|
|
||||||
|
-----------------------------------------------------------[ general ]
|
||||||
|
|
||||||
|
function onError(sErrMsg)
|
||||||
|
Core.SendPmToOps(Core.GetHubSecAlias, "The hub script has broken down!\n"..sErrMsg)
|
||||||
|
end
|
||||||
|
|
||||||
|
function AddUSC(user, mode, menu, cstr)
|
||||||
|
Core.SendToUser(user, "$UserCommand "..mode.." "..menu.."$<%[mynick]> "..cstr.."|")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- @return nil|string
|
||||||
|
function readFile(path)
|
||||||
|
local f = io.open(path, "rb")
|
||||||
|
if (f) then
|
||||||
|
f:close()
|
||||||
|
|
||||||
|
lines = {}
|
||||||
|
for line in io.lines(path) do
|
||||||
|
lines[#lines + 1] = line
|
||||||
|
end
|
||||||
|
return table.concat(lines, "\n")
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- @return string
|
||||||
|
function getRange(array, first, last)
|
||||||
|
local c = first
|
||||||
|
local s = ""
|
||||||
|
while not (c >= last or array[c] == nil) do
|
||||||
|
s = s.."\n"..array[c]
|
||||||
|
c = c + 1
|
||||||
|
end
|
||||||
|
return s
|
||||||
|
end
|
||||||
|
|
||||||
|
function nmdc_escape(message)
|
||||||
|
local r = message:gsub("&", "&"):gsub("|", "|"):gsub("%$", "$") -- escape $ sign
|
||||||
|
return r -- otherwise there are two arguments
|
||||||
|
end
|
||||||
|
|
||||||
|
function nmdc_unescape(message)
|
||||||
|
local r = message:gsub("$", "$"):gsub("|", "|"):gsub("&", "&")
|
||||||
|
return r
|
||||||
|
end
|
BIN
doc/mcs20_redacted.jpg
Normal file
BIN
doc/mcs20_redacted.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 126 KiB |
Reference in New Issue
Block a user