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