no typing by default
various bugfixes blacklisting support (blacklist.lua) json file created automatically users are blacklisted and unblacklisted via reply with /blacklist nicknames support (nick.lua) json file created automatically users set nick by /nick "people" section of config deprecated moderation.lua improvements administrators can now run mod commands administrators are now listed with moderators modlist improved to be smarter and look better administrators can no longer be promoted to moderator /hammer command for admins to perform realm-wide ban
This commit is contained in:
parent
fc4908f033
commit
4c72543315
2
bot.lua
2
bot.lua
@ -22,7 +22,7 @@ function on_msg_receive(msg)
|
|||||||
for i,v in pairs(plugins) do
|
for i,v in pairs(plugins) do
|
||||||
for j,w in pairs(v.triggers) do
|
for j,w in pairs(v.triggers) do
|
||||||
if string.match(lower, w) then
|
if string.match(lower, w) then
|
||||||
if not v.no_typing then
|
if v.typing then
|
||||||
send_chat_action(msg.chat.id, 'typing')
|
send_chat_action(msg.chat.id, 'typing')
|
||||||
end
|
end
|
||||||
local a,b = pcall(function() -- Janky error handling
|
local a,b = pcall(function() -- Janky error handling
|
||||||
|
@ -6,7 +6,7 @@ return {
|
|||||||
time_offset = 0,
|
time_offset = 0,
|
||||||
locale = dofile('loc/en.lua'),
|
locale = dofile('loc/en.lua'),
|
||||||
admins = {
|
admins = {
|
||||||
[0] = 'name'
|
[000] = 'name'
|
||||||
},
|
},
|
||||||
blacklist = load_data('blacklist.json'),
|
blacklist = load_data('blacklist.json'),
|
||||||
plugins = {
|
plugins = {
|
||||||
@ -19,6 +19,7 @@ return {
|
|||||||
'giphy.lua',
|
'giphy.lua',
|
||||||
'xkcd.lua',
|
'xkcd.lua',
|
||||||
'gMaps.lua',
|
'gMaps.lua',
|
||||||
|
'wikipedia.lua',
|
||||||
'imdb.lua',
|
'imdb.lua',
|
||||||
'urbandictionary.lua',
|
'urbandictionary.lua',
|
||||||
'hackernews.lua',
|
'hackernews.lua',
|
||||||
@ -43,16 +44,16 @@ return {
|
|||||||
'whoami.lua',
|
'whoami.lua',
|
||||||
'lmgtfy.lua',
|
'lmgtfy.lua',
|
||||||
'translate.lua',
|
'translate.lua',
|
||||||
'currency.lua'
|
'currency.lua',
|
||||||
},
|
'blacklist.lua',
|
||||||
people = {
|
'nick.lua'
|
||||||
['55994550'] = 'topkecleon'
|
|
||||||
},
|
},
|
||||||
moderation = {
|
moderation = {
|
||||||
realm = -0,
|
realm = -000,
|
||||||
|
realmname = 'Realm name or ident',
|
||||||
data = 'moderation.json',
|
data = 'moderation.json',
|
||||||
admins = {
|
admins = {
|
||||||
[0] = 'nickname',
|
['000'] = 'nickname',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,6 @@ PLUGIN.triggers = {
|
|||||||
'^/admin '
|
'^/admin '
|
||||||
}
|
}
|
||||||
|
|
||||||
PLUGIN.no_typing = true
|
|
||||||
|
|
||||||
function PLUGIN.action(msg)
|
function PLUGIN.action(msg)
|
||||||
|
|
||||||
if msg.date < os.time() - 1 then return end
|
if msg.date < os.time() - 1 then return end
|
||||||
|
@ -36,6 +36,5 @@ end
|
|||||||
return {
|
return {
|
||||||
doc = doc,
|
doc = doc,
|
||||||
triggers = triggers,
|
triggers = triggers,
|
||||||
action = action,
|
action = action
|
||||||
no_typing = true
|
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
local PLUGIN = {}
|
local PLUGIN = {}
|
||||||
|
|
||||||
|
PLUGIN.typing = true
|
||||||
|
|
||||||
PLUGIN.triggers = {
|
PLUGIN.triggers = {
|
||||||
'^@' .. bot.username .. ', ',
|
'^@' .. bot.username .. ', ',
|
||||||
'^' .. bot.first_name .. ', '
|
'^' .. bot.first_name .. ', '
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
local PLUGIN = {}
|
local PLUGIN = {}
|
||||||
|
|
||||||
|
PLUGIN.typing = true -- usually takes a few seconds to load
|
||||||
|
|
||||||
PLUGIN.doc = [[
|
PLUGIN.doc = [[
|
||||||
/hackernews
|
/hackernews
|
||||||
Returns some top stories from Hacker News. Four in a group or eight in a private message.
|
Returns some top stories from Hacker News. Four in a group or eight in a private message.
|
||||||
|
@ -25,7 +25,7 @@ function PLUGIN.action(msg)
|
|||||||
local jstr, res = HTTP.request(url)
|
local jstr, res = HTTP.request(url)
|
||||||
local jdat = JSON.decode(jstr)
|
local jdat = JSON.decode(jstr)
|
||||||
|
|
||||||
if res ~= 200 then
|
if res ~= 200 or not jdat then
|
||||||
return send_msg(msg, config.locale.errors.connection)
|
return send_msg(msg, config.locale.errors.connection)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -5,8 +5,6 @@
|
|||||||
|
|
||||||
local PLUGIN = {}
|
local PLUGIN = {}
|
||||||
|
|
||||||
PLUGIN.no_typing = true
|
|
||||||
|
|
||||||
PLUGIN.triggers = {
|
PLUGIN.triggers = {
|
||||||
bot.first_name .. '%p?$',
|
bot.first_name .. '%p?$',
|
||||||
'^tadaima%p?$',
|
'^tadaima%p?$',
|
||||||
@ -18,18 +16,22 @@ function PLUGIN.action(msg)
|
|||||||
|
|
||||||
local input = string.lower(msg.text)
|
local input = string.lower(msg.text)
|
||||||
|
|
||||||
if config.people[tostring(msg.from.id)] then msg.from.first_name = config.people[tostring(msg.from.id)] end
|
local data = load_data('nicknames.json')
|
||||||
|
local id = tostring(msg.from.id)
|
||||||
|
local nick = msg.from.first_name
|
||||||
|
|
||||||
|
if data[id] then nick = data[id] end
|
||||||
|
|
||||||
for i = 2, #PLUGIN.triggers do
|
for i = 2, #PLUGIN.triggers do
|
||||||
if string.match(input, PLUGIN.triggers[i]) then
|
if string.match(input, PLUGIN.triggers[i]) then
|
||||||
return send_message(msg.chat.id, 'Welcome back, ' .. msg.from.first_name .. '!')
|
return send_message(msg.chat.id, 'Welcome back, ' .. nick .. '!')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for k,v in pairs(config.locale.interactions) do
|
for k,v in pairs(config.locale.interactions) do
|
||||||
for key,val in pairs(v) do
|
for key,val in pairs(v) do
|
||||||
if input:match(val..',? '..bot.first_name) then
|
if input:match(val..',? '..bot.first_name) then
|
||||||
return send_message(msg.chat.id, k:gsub('#NAME', msg.from.first_name))
|
return send_message(msg.chat.id, k:gsub('#NAME', nick))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -19,7 +19,7 @@ help.action = function(msg)
|
|||||||
|
|
||||||
local do_send = false
|
local do_send = false
|
||||||
if data[tostring(msg.chat.id)] and data[tostring(msg.chat.id)][tostring(msg.from.id)] then do_send = true end
|
if data[tostring(msg.chat.id)] and data[tostring(msg.chat.id)][tostring(msg.from.id)] then do_send = true end
|
||||||
if config.moderation.admins[msg.from.id] then do_send = true end
|
if config.moderation.admins[tostring(msg.from.id)] then do_send = true end
|
||||||
if do_send == false then return end
|
if do_send == false then return end
|
||||||
|
|
||||||
local message = [[
|
local message = [[
|
||||||
@ -33,6 +33,7 @@ help.action = function(msg)
|
|||||||
/promote - Promote a user via reply.
|
/promote - Promote a user via reply.
|
||||||
/demote - Demote a user via reply.
|
/demote - Demote a user via reply.
|
||||||
/modcast - Send a broastcast to every group.
|
/modcast - Send a broastcast to every group.
|
||||||
|
/hammer - Ban a user from all groups via reply or username.
|
||||||
]]
|
]]
|
||||||
|
|
||||||
send_message(msg.chat.id, message)
|
send_message(msg.chat.id, message)
|
||||||
@ -49,7 +50,11 @@ ban.action = function(msg)
|
|||||||
local data = load_data(config.moderation.data)
|
local data = load_data(config.moderation.data)
|
||||||
|
|
||||||
if not data[tostring(msg.chat.id)] then return end
|
if not data[tostring(msg.chat.id)] then return end
|
||||||
if not data[tostring(msg.chat.id)][tostring(msg.from.id)] then return end
|
if not data[tostring(msg.chat.id)][tostring(msg.from.id)] then
|
||||||
|
if not config.moderation.admins[tostring(msg.from.id)] then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local target = get_target(msg)
|
local target = get_target(msg)
|
||||||
if not target then
|
if not target then
|
||||||
@ -82,11 +87,15 @@ kick.action = function(msg)
|
|||||||
local data = load_data(config.moderation.data)
|
local data = load_data(config.moderation.data)
|
||||||
|
|
||||||
if not data[tostring(msg.chat.id)] then return end
|
if not data[tostring(msg.chat.id)] then return end
|
||||||
if not data[tostring(msg.chat.id)][tostring(msg.from.id)] then return end
|
if not data[tostring(msg.chat.id)][tostring(msg.from.id)] then
|
||||||
|
if not config.moderation.admins[tostring(msg.from.id)] then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local target = get_target(msg)
|
local target = get_target(msg)
|
||||||
if not target then
|
if not target then
|
||||||
return send_message(msg.chat.id, 'No one to remove.\nTip: Bots must be removed by username.')
|
return send_message(msg.chat.id, 'No one to remove.\nBots must be removed by username.')
|
||||||
end
|
end
|
||||||
|
|
||||||
if msg.reply_to_message and data[tostring(msg.chat.id)][tostring(msg.reply_to_message.from.id)] then
|
if msg.reply_to_message and data[tostring(msg.chat.id)][tostring(msg.reply_to_message.from.id)] then
|
||||||
@ -114,7 +123,7 @@ add.action = function(msg)
|
|||||||
|
|
||||||
local data = load_data(config.moderation.data)
|
local data = load_data(config.moderation.data)
|
||||||
|
|
||||||
if not config.moderation.admins[msg.from.id] then return end
|
if not config.moderation.admins[tostring(msg.from.id)] then return end
|
||||||
|
|
||||||
if data[tostring(msg.chat.id)] then
|
if data[tostring(msg.chat.id)] then
|
||||||
return send_message(msg.chat.id, 'Group is already added.')
|
return send_message(msg.chat.id, 'Group is already added.')
|
||||||
@ -136,7 +145,7 @@ rem.action = function(msg)
|
|||||||
|
|
||||||
local data = load_data(config.moderation.data)
|
local data = load_data(config.moderation.data)
|
||||||
|
|
||||||
if not config.moderation.admins[msg.from.id] then return end
|
if not config.moderation.admins[tostring(msg.from.id)] then return end
|
||||||
|
|
||||||
if not data[tostring(msg.chat.id)] then
|
if not data[tostring(msg.chat.id)] then
|
||||||
return send_message(msg.chat.id, 'Group is not added.')
|
return send_message(msg.chat.id, 'Group is not added.')
|
||||||
@ -157,10 +166,11 @@ promote.trigger = '^/[mod]*prom[ote]*$'
|
|||||||
promote.action = function(msg)
|
promote.action = function(msg)
|
||||||
|
|
||||||
local data = load_data(config.moderation.data)
|
local data = load_data(config.moderation.data)
|
||||||
|
local chatid = tostring(msg.chat.id)
|
||||||
|
|
||||||
if not config.moderation.admins[msg.from.id] then return end
|
if not config.moderation.admins[tostring(msg.from.id)] then return end
|
||||||
|
|
||||||
if not data[tostring(msg.chat.id)] then
|
if not data[chatid] then
|
||||||
return send_message(msg.chat.id, 'Group is not added.')
|
return send_message(msg.chat.id, 'Group is not added.')
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -168,15 +178,21 @@ promote.action = function(msg)
|
|||||||
return send_message(msg.chat.id, 'Promotions must be done via reply.')
|
return send_message(msg.chat.id, 'Promotions must be done via reply.')
|
||||||
end
|
end
|
||||||
|
|
||||||
if data[tostring(msg.chat.id)][tostring(msg.reply_to_message.from.id)] then
|
local targid = tostring(msg.reply_to_message.from.id)
|
||||||
|
|
||||||
|
if data[chatid][targid] then
|
||||||
return send_message(msg.chat.id, msg.reply_to_message.from.first_name..' is already a moderator.')
|
return send_message(msg.chat.id, msg.reply_to_message.from.first_name..' is already a moderator.')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if config.moderation.admins[targid] then
|
||||||
|
return send_message(msg.chat.id, 'Administrators do not need to be promoted.')
|
||||||
|
end
|
||||||
|
|
||||||
if not msg.reply_to_message.from.username then
|
if not msg.reply_to_message.from.username then
|
||||||
msg.reply_to_message.from.username = msg.reply_to_message.from.first_name
|
msg.reply_to_message.from.username = msg.reply_to_message.from.first_name
|
||||||
end
|
end
|
||||||
|
|
||||||
data[tostring(msg.chat.id)][tostring(msg.reply_to_message.from.id)] = msg.reply_to_message.from.first_name
|
data[chatid][targid] = msg.reply_to_message.from.first_name
|
||||||
save_data(config.moderation.data, data)
|
save_data(config.moderation.data, data)
|
||||||
|
|
||||||
send_message(msg.chat.id, msg.reply_to_message.from.first_name..' has been promoted.')
|
send_message(msg.chat.id, msg.reply_to_message.from.first_name..' has been promoted.')
|
||||||
@ -192,7 +208,7 @@ demote.action = function(msg)
|
|||||||
|
|
||||||
local data = load_data(config.moderation.data)
|
local data = load_data(config.moderation.data)
|
||||||
|
|
||||||
if not config.moderation.admins[msg.from.id] then return end
|
if not config.moderation.admins[tostring(msg.from.id)] then return end
|
||||||
|
|
||||||
if not data[tostring(msg.chat.id)] then
|
if not data[tostring(msg.chat.id)] then
|
||||||
return send_message(msg.chat.id, 'Group is not added.')
|
return send_message(msg.chat.id, 'Group is not added.')
|
||||||
@ -227,7 +243,7 @@ broadcast.action = function(msg)
|
|||||||
|
|
||||||
local data = load_data(config.moderation.data)
|
local data = load_data(config.moderation.data)
|
||||||
|
|
||||||
if not config.moderation.admins[msg.from.id] then return end
|
if not config.moderation.admins[tostring(msg.from.id)] then return end
|
||||||
|
|
||||||
if msg.chat.id ~= config.moderation.realm then
|
if msg.chat.id ~= config.moderation.realm then
|
||||||
return send_message(msg.chat.id, 'This command must be run in the admin group.')
|
return send_message(msg.chat.id, 'This command must be run in the admin group.')
|
||||||
@ -258,10 +274,19 @@ modlist.action = function(msg)
|
|||||||
return send_message(msg.chat.id, 'Group is not added.')
|
return send_message(msg.chat.id, 'Group is not added.')
|
||||||
end
|
end
|
||||||
|
|
||||||
local message = 'List of moderators for ' .. msg.chat.title .. ':\n'
|
local message = ''
|
||||||
|
|
||||||
for k,v in pairs(data[tostring(msg.chat.id)]) do
|
for k,v in pairs(data[tostring(msg.chat.id)]) do
|
||||||
message = message .. v .. ' (' .. k .. ')\n'
|
message = message ..' - '..v.. ' (' .. k .. ')\n'
|
||||||
|
end
|
||||||
|
|
||||||
|
if message ~= '' then
|
||||||
|
message = 'Moderators for ' .. msg.chat.title .. ':\n' .. message .. '\n'
|
||||||
|
end
|
||||||
|
|
||||||
|
message = message .. 'Administrators for ' .. config.moderation.realmname .. ':\n'
|
||||||
|
for k,v in pairs(config.moderation.admins) do
|
||||||
|
message = message ..' - '..v.. ' (' .. k .. ')\n'
|
||||||
end
|
end
|
||||||
|
|
||||||
send_message(msg.chat.id, message)
|
send_message(msg.chat.id, message)
|
||||||
@ -269,6 +294,30 @@ modlist.action = function(msg)
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local badmin = {}
|
||||||
|
|
||||||
|
badmin.trigger = '^/hammer'
|
||||||
|
|
||||||
|
badmin.action = function(msg)
|
||||||
|
|
||||||
|
if not config.moderation.admins[tostring(msg.from.id)] then return end
|
||||||
|
|
||||||
|
local target = get_target(msg)
|
||||||
|
if not target then
|
||||||
|
return send_message(msg.chat.id, 'No one to remove.\nBots must be removed by username.')
|
||||||
|
end
|
||||||
|
|
||||||
|
send_message(config.moderation.realm, '/ban ' .. target .. ' from all')
|
||||||
|
|
||||||
|
if msg.reply_to_message then
|
||||||
|
target = msg.reply_to_message.from.first_name
|
||||||
|
end
|
||||||
|
|
||||||
|
send_message(config.moderation.realm, target .. ' was banhammered by ' .. msg.from.first_name .. '.')
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
local modactions = {
|
local modactions = {
|
||||||
help,
|
help,
|
||||||
ban,
|
ban,
|
||||||
@ -278,7 +327,8 @@ local modactions = {
|
|||||||
promote,
|
promote,
|
||||||
demote,
|
demote,
|
||||||
broadcast,
|
broadcast,
|
||||||
modlist
|
modlist,
|
||||||
|
badmin
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -291,7 +341,8 @@ local triggers = {
|
|||||||
'^/[mod]*prom[ote]*$',
|
'^/[mod]*prom[ote]*$',
|
||||||
'^/[mod]*dem[ote]*',
|
'^/[mod]*dem[ote]*',
|
||||||
'^/modkick',
|
'^/modkick',
|
||||||
'^/modban'
|
'^/modban',
|
||||||
|
'^/hammer'
|
||||||
}
|
}
|
||||||
|
|
||||||
local action = function(msg)
|
local action = function(msg)
|
||||||
@ -304,6 +355,5 @@ end
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
triggers = triggers,
|
triggers = triggers,
|
||||||
action = action,
|
action = action
|
||||||
no_typing = true
|
|
||||||
}
|
}
|
||||||
|
29
plugins/nick.lua
Normal file
29
plugins/nick.lua
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
local doc = [[
|
||||||
|
/nick <nickname>
|
||||||
|
Set your nickname for the bot to call you.
|
||||||
|
]]
|
||||||
|
|
||||||
|
local triggers = {
|
||||||
|
'^/nick'
|
||||||
|
}
|
||||||
|
|
||||||
|
local action = function(msg)
|
||||||
|
|
||||||
|
local data = load_data('nicknames.json')
|
||||||
|
local id = tostring(msg.from.id)
|
||||||
|
local input = get_input(msg.text)
|
||||||
|
if not input then
|
||||||
|
return send_msg(msg, doc)
|
||||||
|
end
|
||||||
|
|
||||||
|
data[id] = input
|
||||||
|
save_data('nicknames.json', data)
|
||||||
|
send_msg(msg, 'Your nickname has been set to ' .. input .. '.')
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
doc = doc,
|
||||||
|
triggers = triggers,
|
||||||
|
action = action
|
||||||
|
}
|
@ -86,7 +86,7 @@ function PLUGIN.getSlap(slapper, victim)
|
|||||||
slapper .. " hit " .. victim .. " with a small, interstellar spaceship.",
|
slapper .. " hit " .. victim .. " with a small, interstellar spaceship.",
|
||||||
victim .. " was quickscoped by " .. slapper .. ".",
|
victim .. " was quickscoped by " .. slapper .. ".",
|
||||||
slapper .. " put " .. victim .. " in check-mate.",
|
slapper .. " put " .. victim .. " in check-mate.",
|
||||||
slapper .. " encrypted " .. victim .. " and deleted the private key.",
|
slapper .. " RSA-encrypted " .. victim .. " and deleted the private key.",
|
||||||
slapper .. " put " .. victim .. " in the friendzone.",
|
slapper .. " put " .. victim .. " in the friendzone.",
|
||||||
slapper .. " slaps " .. victim .. " with a DMCA takedown request!",
|
slapper .. " slaps " .. victim .. " with a DMCA takedown request!",
|
||||||
victim .. " became a corpse blanket for " .. slapper .. ".",
|
victim .. " became a corpse blanket for " .. slapper .. ".",
|
||||||
|
58
plugins/wikipedia.lua
Normal file
58
plugins/wikipedia.lua
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
local doc = [[
|
||||||
|
/wiki <topic>
|
||||||
|
Search Wikipedia for a relevant article and return its summary.
|
||||||
|
]]
|
||||||
|
|
||||||
|
local triggers = {
|
||||||
|
'^/wiki',
|
||||||
|
'^/w '
|
||||||
|
}
|
||||||
|
|
||||||
|
local action = function(msg)
|
||||||
|
|
||||||
|
local gurl = 'http://ajax.googleapis.com/ajax/services/search/web?v=1.0&rsz=1&q=site:wikipedia.org%20'
|
||||||
|
local wurl = 'http://en.wikipedia.org/w/api.php?action=query&prop=extracts&format=json&exchars=4000&exsectionformat=plain&titles='
|
||||||
|
|
||||||
|
local input = get_input(msg.text)
|
||||||
|
if not input then
|
||||||
|
return send_msg(msg, doc)
|
||||||
|
end
|
||||||
|
|
||||||
|
local jstr, res = HTTP.request(gurl..URL.escape(input))
|
||||||
|
if res ~= 200 then
|
||||||
|
return send_msg(msg, config.locale.errors.connection)
|
||||||
|
end
|
||||||
|
local title = JSON.decode(jstr)
|
||||||
|
local url = title.responseData.results[1].url
|
||||||
|
title = title.responseData.results[1].titleNoFormatting
|
||||||
|
title = title:gsub(' %- Wikipedia, the free encyclopedia', '')
|
||||||
|
|
||||||
|
jstr, res = HTTPS.request(wurl..URL.escape(title))
|
||||||
|
if res ~= 200 then
|
||||||
|
return send_msg(msg, config.locale.errors.connection)
|
||||||
|
end
|
||||||
|
local text = JSON.decode(jstr).query.pages
|
||||||
|
|
||||||
|
for k,v in pairs(text) do
|
||||||
|
text = v.extract
|
||||||
|
break -- Seriously, there's probably a way more elegant solution.
|
||||||
|
end
|
||||||
|
|
||||||
|
text = text:gsub('</?.>', '')
|
||||||
|
local l = text:find('<h2>')
|
||||||
|
if l then
|
||||||
|
text = text:sub(1, l-2)
|
||||||
|
end
|
||||||
|
|
||||||
|
text = text .. '\n' .. url
|
||||||
|
|
||||||
|
send_msg(msg, text)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
doc = doc,
|
||||||
|
triggers = triggers,
|
||||||
|
action = action,
|
||||||
|
typing = true
|
||||||
|
}
|
Reference in New Issue
Block a user