mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-16 13:23:46 +03:00
644 lines
17 KiB
Lua
644 lines
17 KiB
Lua
--[[
|
||
| This file was obtained through the combined efforts
|
||
| of Madbluntz & Plymouth Antiquarian Society.
|
||
|
|
||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||
| Maloy, DrPepper10 @ RIP, Atle!
|
||
|
|
||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||
--]]
|
||
|
||
local bit = bit
|
||
local IsValid = IsValid
|
||
local pairs = pairs
|
||
local table = table
|
||
local L = L
|
||
local netstream = netstream
|
||
local Entity = Entity
|
||
local print = print
|
||
local math = math
|
||
local tostring = tostring
|
||
local CAMI = CAMI
|
||
local Vector = Vector
|
||
local game = game
|
||
local RunConsoleCommand = RunConsoleCommand
|
||
local net = net
|
||
local player = player
|
||
local ipairs = ipairs
|
||
local ents = ents
|
||
local string = string
|
||
local timer = timer
|
||
local ix = ix
|
||
|
||
local PLUGIN = PLUGIN
|
||
|
||
--Links
|
||
ix.command.Add("Discord", {
|
||
description = "Obtenir un lien vers le serveur Discord",
|
||
privilege = "Basic Commands",
|
||
OnRun = function(self, client)
|
||
net.Start("ixOpenURL")
|
||
net.WriteString(ix.config.Get("DiscordLink"))
|
||
net.Send(client)
|
||
end,
|
||
bNoIndicator = true
|
||
})
|
||
ix.command.Add("Content", {
|
||
description = "Obtenir un lien vers le Workshop",
|
||
privilege = "Basic Commands",
|
||
OnRun = function(self, client)
|
||
net.Start("ixOpenURL")
|
||
net.WriteString(ix.config.Get("ContentLink"))
|
||
net.Send(client)
|
||
end
|
||
})
|
||
ix.command.Add("Forum", {
|
||
description = "Obtenir un lien vers les forums",
|
||
privilege = "Basic Commands",
|
||
OnRun = function(self, client)
|
||
net.Start("ixOpenURL")
|
||
net.WriteString(ix.config.Get("ForumLink"))
|
||
net.Send(client)
|
||
end,
|
||
bNoIndicator = true
|
||
})
|
||
|
||
--Basic Admin
|
||
ix.command.Add("PGI", {
|
||
description = "Obtenez les informations de base de quelqu'un et copiez son SteamID.",
|
||
arguments = {
|
||
bit.bor(ix.type.player, ix.type.optional)
|
||
},
|
||
--alias = "PlyGetInfo",
|
||
privilege = "Commandes d'administration de base",
|
||
OnRun = function(self, client, target)
|
||
if (!target) then
|
||
target = client:GetEyeTraceNoCursor().Entity
|
||
end
|
||
|
||
if (!IsValid(target) or !target:IsPlayer()) then
|
||
client:NotifyLocalized("bastionPGIInvalidTarget")
|
||
return
|
||
end
|
||
|
||
net.Start("ixPlayerInfo")
|
||
net.WriteEntity(target)
|
||
net.Send(client)
|
||
end,
|
||
bNoIndicator = true
|
||
})
|
||
|
||
ix.command.Add("PrintStaffList", {
|
||
alias = "PSL",
|
||
description = "Imprimez une liste des membres du personnel en ligne.",
|
||
OnRun = function(self, client)
|
||
local staff = {}
|
||
local bFound = false
|
||
for _, v in ipairs(player.GetAll()) do
|
||
local incognitoSetting = ix.option.Get(v, "iconIncognitoMode", false)
|
||
local iconIncognitoMode = !client:IsAdmin() and !client:IsSuperAdmin() and incognitoSetting
|
||
if (v:IsAdmin() or v:IsSuperAdmin()) and !iconIncognitoMode then
|
||
local userGroup = v:GetUserGroup()
|
||
staff[userGroup] = staff[userGroup] or {}
|
||
staff[userGroup][#staff[userGroup] + 1] = v
|
||
bFound = true
|
||
end
|
||
end
|
||
|
||
local toSend = {}
|
||
for k, v in pairs(staff) do
|
||
toSend[#toSend + 1] = {name = k, players = v}
|
||
end
|
||
table.SortByMember(toSend, "name", true)
|
||
|
||
if (bFound) then
|
||
net.Start("ixStaffList")
|
||
net.WriteUInt(#toSend, 8)
|
||
for _, v in ipairs(toSend) do
|
||
net.WriteString(v.name)
|
||
net.WriteUInt(#v.players, 8)
|
||
for i = 1, #v.players do
|
||
net.WriteEntity(v.players[i])
|
||
end
|
||
end
|
||
net.Send(client)
|
||
else
|
||
client:Notify("Il n'y a pas de STAFF en ligne actuellement.")
|
||
end
|
||
end,
|
||
bNoIndicator = true
|
||
})
|
||
|
||
ix.command.Add("PrintFactionList", {
|
||
alias = "PFL",
|
||
description = "Imprimer une liste des membres d'une faction actuellement en ligne (y compris sur un autre personnage).",
|
||
arguments = {
|
||
ix.type.string
|
||
},
|
||
privilege = "Basic Admin Commands",
|
||
OnRun = function(self, client, name)
|
||
if (name == "") then
|
||
return "@invalidArg", 2
|
||
end
|
||
|
||
local faction = ix.faction.teams[name]
|
||
|
||
if (!faction) then
|
||
for _, v in ipairs(ix.faction.indices) do
|
||
if (ix.util.StringMatches(L(v.name, client), name) or ix.util.StringMatches(v.uniqueID, name)) then
|
||
faction = v
|
||
|
||
break
|
||
end
|
||
end
|
||
end
|
||
|
||
if (faction) then
|
||
local players = {}
|
||
for _, v in ipairs(player.GetAll()) do
|
||
if (v:HasWhitelist(faction.index)) then
|
||
players[#players + 1] = v
|
||
end
|
||
end
|
||
net.Start("ixStaffList")
|
||
net.WriteUInt(1, 8)
|
||
net.WriteString(faction.name)
|
||
net.WriteUInt(#players, 8)
|
||
for i = 1, #players do
|
||
net.WriteEntity(players[i])
|
||
end
|
||
net.Send(client)
|
||
else
|
||
return "@invalidFaction"
|
||
end
|
||
end,
|
||
bNoIndicator = true
|
||
})
|
||
|
||
ix.command.Add("PlaySound", {
|
||
description = "Jouez un son pour tous les joueurs (lorsqu'aucune portée n'est donnée) ou ceux qui sont proches de vous.",
|
||
arguments = {
|
||
ix.type.string,
|
||
bit.bor(ix.type.number, ix.type.optional)
|
||
},
|
||
privilege = "Basic Admin Commands",
|
||
OnRun = function(self, client, sound, range)
|
||
local targets = range and {} or player.GetAll()
|
||
if (range) then
|
||
range = range * range
|
||
local clientPos = client:EyePos()
|
||
for _, target in ipairs(player.GetAll()) do
|
||
if (target:EyePos():DistToSqr(clientPos) < range) then
|
||
targets[#targets + 1] = target
|
||
end
|
||
end
|
||
end
|
||
|
||
net.Start("ixPlaySound")
|
||
net.WriteString(PLUGIN.soundAlias[sound] or sound)
|
||
net.Send(targets)
|
||
end,
|
||
indicator = "chatPerforming"
|
||
})
|
||
|
||
ix.command.Add("ScreenShake", {
|
||
description = "Shakes the screen of everyone in the specified range. Specify the amplitude, the frequency and the radius for it to work.",
|
||
arguments = {
|
||
ix.type.number,
|
||
ix.type.number,
|
||
ix.type.number,
|
||
ix.type.number
|
||
},
|
||
argumentNames = {"time(seconds)", "amplitude", "frequency", "radius"},
|
||
privilege = "Basic Admin Commands",
|
||
OnRun = function(self, client, seconds, strength, frequency, radius)
|
||
local vector = client:GetPos()
|
||
util.ScreenShake(vector, strength or 5, frequency or 5, seconds, radius)
|
||
end,
|
||
indicator = "chatPerforming"
|
||
})
|
||
|
||
ix.command.Add("PlaySoundGlobal", {
|
||
description = "Jouez un son pour tous les joueurs.",
|
||
arguments = {
|
||
ix.type.string,
|
||
},
|
||
privilege = "Basic Admin Commands",
|
||
OnRun = function(self, client, sound)
|
||
net.Start("ixPlaySound")
|
||
net.WriteString(PLUGIN.soundAlias[sound] or sound)
|
||
net.WriteBool(true)
|
||
net.Send(player.GetAll())
|
||
end,
|
||
indicator = "chatPerforming"
|
||
})
|
||
|
||
ix.command.Add("ShowEdicts", {
|
||
description = "Renvoie le nombre d'entités en réseau actuellement sur le serveur.",
|
||
privilege = "Basic Admin Commands",
|
||
OnRun = function(self, client)
|
||
local edictsCount = ents.GetEdictCount()
|
||
local edictsLeft = 8192 - edictsCount
|
||
|
||
return string.format("Il y a actuellement %s édits sur le serveur. Vous pouvez avoir jusqu'à %s de plus.", edictsCount, edictsLeft)
|
||
end,
|
||
bNoIndicator = true
|
||
})
|
||
|
||
ix.command.Add("ShowEntsInRadius", {
|
||
description = "Affiche une liste d'entités dans un rayon donné.",
|
||
privilege = "Basic Admin Commands",
|
||
arguments = {ix.type.number},
|
||
OnRun = function(self, client, radius)
|
||
local entities = {}
|
||
local pos = client:GetPos()
|
||
for _, v in pairs(ents.FindInSphere(pos, radius)) do
|
||
if (!IsValid(v)) then continue end
|
||
entities[#entities + 1] = table.concat({v:EntIndex(), v:GetClass(), v:GetModel() or "no model", v:GetPos():Distance(pos), v:MapCreationID()}, ", ")
|
||
end
|
||
netstream.Start(client, "ixShowEntsInRadius", table.concat(entities,"\n"))
|
||
client:NotifyLocalized("entsPrintedInConsole")
|
||
end,
|
||
bNoIndicator = true,
|
||
})
|
||
|
||
ix.command.Add("RemoveEntityByID", {
|
||
description = "Affiche une liste d'entités dans un rayon donné.",
|
||
superAdminOnly = true,
|
||
arguments = {ix.type.number},
|
||
OnRun = function(self, client, number)
|
||
local entity = Entity(number)
|
||
if (IsValid(entity)) then
|
||
client:NotifyLocalized("entityRemoved", number, entity:GetClass())
|
||
entity:Remove()
|
||
else
|
||
client:NotifyLocalized("entityNotFound", number)
|
||
end
|
||
end,
|
||
bNoIndicator = true,
|
||
})
|
||
|
||
if (CLIENT) then
|
||
netstream.Hook("ixShowEntsInRadius", function(text)
|
||
print(text)
|
||
end)
|
||
end
|
||
|
||
ix.command.Add("LocalEvent", {
|
||
description = "@cmdLocalEvent",
|
||
privilege = "Event",
|
||
arguments = {
|
||
ix.type.string,
|
||
bit.bor(ix.type.number, ix.type.optional)
|
||
},
|
||
OnRun = function(self, client, event, range)
|
||
local _range = range or (ix.config.Get("chatRange", 280) * 2)
|
||
|
||
ix.chat.classes.localevent.range = _range * _range
|
||
|
||
local doubleCommand = false
|
||
|
||
-- This could probably be done a bit smarter but I'm sure it'll do.
|
||
if (string.Left(string.lower(event), 11) == "/localevent") then
|
||
doubleCommand = true
|
||
|
||
event = string.Right(event, #event - 12)
|
||
elseif (string.Left(string.lower(event), 10) == "localevent") then
|
||
doubleCommand = true
|
||
|
||
event = string.Right(event, #event - 11)
|
||
end
|
||
|
||
if (doubleCommand) then
|
||
client:NotifyLocalized("textDoubleCommand", "/LocalEvent")
|
||
end
|
||
|
||
ix.chat.Send(client, "localevent", event)
|
||
end,
|
||
indicator = "chatPerforming"
|
||
})
|
||
|
||
ix.command.Add("RemovePersistedProps", {
|
||
description = "@cmdRemovePersistedProps",
|
||
superAdminOnly = true,
|
||
arguments = {
|
||
ix.type.number
|
||
},
|
||
OnRun = function(self, client, radius)
|
||
if (radius < 0) then
|
||
client:Notify("Radius must be a positive number!")
|
||
|
||
return
|
||
end
|
||
|
||
for _, entity in ipairs(ents.FindInSphere(client:GetPos(), radius)) do
|
||
if (!entity:GetNetVar("Persistent")) then continue end
|
||
|
||
entity:Remove()
|
||
end
|
||
|
||
client:Notify("Removed all persisted props in a radius of " .. radius .. " units.")
|
||
end
|
||
})
|
||
|
||
ix.command.Add("Announce", {
|
||
description = "@cmdAnnounce",
|
||
arguments = {
|
||
ix.type.text
|
||
},
|
||
OnRun = function(self, client, event)
|
||
ix.chat.Send(client, "announcement", event)
|
||
end,
|
||
OnCheckAccess = function(self, client)
|
||
return !client or CAMI.PlayerHasAccess(client, "Helix - Basic Admin Commands")
|
||
end,
|
||
indicator = "chatTyping"
|
||
})
|
||
|
||
|
||
-- Help
|
||
ix.command.Add("Help", {
|
||
description = "@cmdStaffHelp",
|
||
arguments = ix.type.text,
|
||
alias = {"GMHelp", "ModHelp"},
|
||
OnRun = function(self, client, text)
|
||
client:Say("@ " .. text)
|
||
end,
|
||
OnCheckAccess = function(self, client)
|
||
return !CAMI.PlayerHasAccess(client, "Helix - Hear Staff Chat")
|
||
end,
|
||
indicator = "chatTyping"
|
||
})
|
||
|
||
-- Admin Chat
|
||
ix.command.Add("Staff", {
|
||
description = "Discutez avec d'autres membres du STAFF.",
|
||
arguments = ix.type.text,
|
||
privilege = "Hear Staff Chat",
|
||
alias = {"Op", "Mod", "ModChat"},
|
||
OnRun = function(self, client, text)
|
||
ix.chat.Send(client, "staff_chat", text)
|
||
end,
|
||
indicator = "chatTyping"
|
||
})
|
||
|
||
-- Gamemaster Chat
|
||
ix.command.Add("GameMaster", {
|
||
description = "Discutez avec d'autres Gamemasters.",
|
||
arguments = ix.type.text,
|
||
privilege = "Hear GM Chat",
|
||
alias = {"GMChat", "GM"},
|
||
OnRun = function(self, client, text)
|
||
ix.chat.Send(client, "gm_chat", text)
|
||
end,
|
||
indicator = "chatTyping"
|
||
})
|
||
|
||
-- Mentor Chat
|
||
ix.command.Add("Mentor", {
|
||
description = "Discutez avec d'autres mentors.",
|
||
arguments = ix.type.text,
|
||
privilege = "Hear Mentor Chat",
|
||
alias = {"MChat", "M"},
|
||
OnRun = function(self, client, text)
|
||
ix.chat.Send(client, "mentor_chat", text)
|
||
end,
|
||
indicator = "chatTyping"
|
||
})
|
||
|
||
-- Fun Stuff
|
||
ix.command.Add("Achievement", {
|
||
description = "Quelqu'un a gagné une réalisation spéciale !",
|
||
arguments = {
|
||
ix.type.player,
|
||
ix.type.text
|
||
},
|
||
privilege = "Fun Stuff",
|
||
OnRun = function(self, client, target, text)
|
||
ix.chat.Send(client, "achievement_get", text, false, nil,
|
||
{target, "ambient/water/drip" .. math.random( 1, 4 ) .. ".wav"})
|
||
end,
|
||
indicator = "chatTyping"
|
||
})
|
||
ix.command.Add("DarwinAward", {
|
||
description = "Quelqu'un a mérité un exploit : il a fait le sacrifice ultime pour augmenter le QI moyen de l'humanité.",
|
||
arguments = {
|
||
ix.type.player
|
||
},
|
||
privilege = "Fun Stuff",
|
||
OnRun = function(self, client, target)
|
||
if (!target:Alive()) then
|
||
local pos = target:GetPos()
|
||
target:Spawn()
|
||
|
||
target:SetPos(pos)
|
||
end
|
||
target:SetMoveType(MOVETYPE_WALK)
|
||
target:SetVelocity(Vector(0, 0, 4000))
|
||
|
||
timer.Simple(1, function() PLUGIN:Explode(target) end)
|
||
ix.chat.Send(client, "achievement_get", "DARWIN AWARD", false, nil,
|
||
{target, "ambient/alarms/razortrain_horn1.wav"})
|
||
end,
|
||
indicator = "chatTyping"
|
||
})
|
||
ix.command.Add("PlyRocket", {
|
||
description = "To infinity, and beyond!.",
|
||
arguments = {
|
||
ix.type.player
|
||
},
|
||
privilege = "Fun Stuff",
|
||
OnRun = function(self, client, target)
|
||
if (!target:Alive()) then
|
||
local pos = target:GetPos()
|
||
target:Spawn()
|
||
|
||
target:SetPos(pos)
|
||
end
|
||
target:SetMoveType(MOVETYPE_WALK)
|
||
target:SetVelocity(Vector(0, 0, 4000))
|
||
|
||
timer.Simple(1, function() PLUGIN:Explode(target) end)
|
||
end,
|
||
bNoIndicator = true
|
||
})
|
||
|
||
ix.command.Add("SetTimeScale", {
|
||
description = "@cmdTimeScale",
|
||
arguments = {
|
||
bit.bor(ix.type.number, ix.type.optional)
|
||
},
|
||
privilege = "Fun Stuff",
|
||
OnRun = function(self, client, number)
|
||
local scale = math.Clamp(number or 1, 0.001, 5)
|
||
game.SetTimeScale(scale)
|
||
|
||
for _, v in ipairs(player.GetAll()) do
|
||
if (self:OnCheckAccess(v)) then
|
||
v:NotifyLocalized("bastionTimeScale", client:Name(), scale)
|
||
end
|
||
end
|
||
end,
|
||
bNoIndicator = true
|
||
})
|
||
|
||
ix.command.Add("SetGravity", {
|
||
description = "@cmdGravity",
|
||
arguments = {
|
||
bit.bor(ix.type.number, ix.type.optional)
|
||
},
|
||
privilege = "Fun Stuff",
|
||
OnRun = function(self, client, number)
|
||
RunConsoleCommand("sv_gravity", number)
|
||
|
||
for _, v in ipairs(player.GetAll()) do
|
||
if (self:OnCheckAccess(v)) then
|
||
v:NotifyLocalized("bastionGravity", client:Name(), number)
|
||
end
|
||
end
|
||
end,
|
||
bNoIndicator = true
|
||
})
|
||
|
||
-- lookup commands
|
||
ix.command.Add("LookupSteamID", {
|
||
description = "Lookup a SteamID in the Bastion user database",
|
||
arguments = {
|
||
ix.type.text
|
||
},
|
||
privilege = "Bastion Lookup",
|
||
OnRun = function(self, client, target)
|
||
if (string.find(target, "^STEAM_%d+:%d+:%d+$")) then
|
||
PLUGIN:LookupSteamID(client, target)
|
||
return
|
||
elseif (string.len(target) == 17 and string.find(target, "^%d+$")) then
|
||
PLUGIN:LookupSteamID(client, target)
|
||
return
|
||
end
|
||
|
||
target = ix.util.FindPlayer(target, false)
|
||
client:NotifyLocalized("bastionTargetSelected", target:Name())
|
||
|
||
PLUGIN:LookupSteamID(client, target:SteamID64())
|
||
end,
|
||
bNoIndicator = true
|
||
})
|
||
|
||
ix.command.Add("LookupIPUsers", {
|
||
description = "Lookup a SteamID in the Bastion IP database",
|
||
arguments = {
|
||
ix.type.text
|
||
},
|
||
privilege = "Bastion Lookup",
|
||
OnRun = function(self, client, target)
|
||
if (string.find(target, "^STEAM_%d+:%d+:%d+$")) then
|
||
PLUGIN:LookupIPUsers(client, target)
|
||
return
|
||
elseif (string.len(target) == 17 and string.find(target, "^%d+$")) then
|
||
PLUGIN:LookupIPUsers(client, target)
|
||
return
|
||
end
|
||
|
||
target = ix.util.FindPlayer(target, false)
|
||
client:NotifyLocalized("bastionTargetSelected", target:Name())
|
||
|
||
PLUGIN:LookupIPUsers(client, target:SteamID64())
|
||
end,
|
||
bNoIndicator = true
|
||
})
|
||
|
||
ix.command.Add("ProxyWhitelist", {
|
||
description = "Whitelist a player as trusted to disable future proxy checks.",
|
||
arguments = {
|
||
ix.type.player
|
||
},
|
||
privilege = "Bastion Whitelist",
|
||
OnRun = function(self, client, target)
|
||
if (PLUGIN:WhitelistPlayer(target)) then
|
||
client:NotifyLocalized("whitelistDone")
|
||
else
|
||
client:NotifyLocalized("whitelistError")
|
||
end
|
||
end,
|
||
indicator = "chatTyping"
|
||
})
|
||
|
||
ix.command.Add("PlyGetCharacters", {
|
||
description = "Get a list of a player's characters.",
|
||
arguments = {
|
||
ix.type.player
|
||
},
|
||
adminOnly = true,
|
||
OnRun = function(self, client, target)
|
||
client:ChatNotify(target:SteamName() .. "'s characters:")
|
||
client:ChatNotify("====================")
|
||
|
||
for _, character in pairs(target.ixCharList) do
|
||
client:ChatNotify(ix.char.loaded[character].vars.name)
|
||
end
|
||
end
|
||
})
|
||
|
||
|
||
ix.command.Add("LTARP", {
|
||
description = "Automatically ban a player for leaving to avoid RP after a grace period.",
|
||
arguments = {
|
||
ix.type.string,
|
||
ix.type.number,
|
||
bit.bor(ix.type.text, ix.type.optional),
|
||
},
|
||
adminOnly = true,
|
||
OnRun = function(self, client, steamID, days, reason)
|
||
local target = player.GetBySteamID64(steamID) or player.GetBySteamID(steamID)
|
||
if (target) then
|
||
client:Notify(target:SteamName().." is still on the server as '"..target:Name().."!")
|
||
return
|
||
end
|
||
|
||
if (days > 100) then
|
||
client:Notify("Invalid duration entered. Max ban length is 100 days.")
|
||
return
|
||
end
|
||
|
||
if (string.find(string.upper(steamID), "^STEAM")) then
|
||
steamID = util.SteamIDTo64(string.upper(steamID))
|
||
end
|
||
|
||
if (!PLUGIN.disconnects[steamID]) then
|
||
client:Notify("Could not find a disconnect for "..steamID..", manually ban them if needed.")
|
||
return
|
||
end
|
||
|
||
local info = PLUGIN.disconnects[steamID]
|
||
if (info.timer) then
|
||
client:Notify(steamID.." is already tagged for LTARP by "..info.bannedByName..".")
|
||
return
|
||
end
|
||
|
||
local reconnectTime, maxBanDelay = 30, 40
|
||
if (info.time + reconnectTime * 60 < os.time()) then
|
||
if (info.time + maxBanDelay * 60 < os.time()) then
|
||
client:Notify(steamID.." disconnected more than "..maxBanDelay.." minutes ago already. Manually ban them if needed.")
|
||
return
|
||
end
|
||
|
||
RunConsoleCommand("sam", "banid", steamID, tostring(days * 60 * 24), reason != "" and reason or "Leaving to avoid RP. Appeal on willard.network ~"..client:SteamName())
|
||
return
|
||
end
|
||
|
||
local uniqueID = "ixLTARP"..steamID
|
||
local bannedBy = client and client:SteamName() or "console"
|
||
timer.Create(uniqueID, info.time - os.time() + reconnectTime * 60, 1, function()
|
||
if (IsValid(client)) then
|
||
client:Notify(steamID.." did not reconnect and has been banned.")
|
||
end
|
||
RunConsoleCommand("sam", "banid", steamID, tostring(days * 60 * 24), reason != "" and reason or "Leaving to avoid RP. Appeal on willard.network ~"..bannedBy)
|
||
end)
|
||
|
||
info.timer = uniqueID
|
||
info.bannedBy = client
|
||
info.bannedByName = client and client:SteamName() or "console"
|
||
client:Notify("Timer set! "..steamID.." will be banned in "..math.ceil((info.time - os.time() + reconnectTime * 60) / 60).." minutes.")
|
||
end
|
||
})
|