Extracted config from instance.

This commit is contained in:
Brayden Banks 2016-05-26 17:26:30 -07:00
parent a1810dba86
commit 6a13d523fb
34 changed files with 229 additions and 228 deletions

27
bot.lua
View File

@ -6,18 +6,16 @@ local utilities -- Load miscellaneous and cross-plugin functions.
bot.version = '3.8'
function bot:init() -- The function run when the bot is started or reloaded.
function bot:init(config) -- The function run when the bot is started or reloaded.
bindings = require('bindings')
utilities = require('utilities')
self.config = require('config') -- Load configuration file.
assert(
self.config.bot_api_key and self.config.bot_api_key ~= '',
'You did not set your bot token in config.lua!'
config.bot_api_key and config.bot_api_key ~= '',
'You did not set your bot token in the config!'
)
self.BASE_URL = 'https://api.telegram.org/bot' .. self.config.bot_api_key .. '/'
self.BASE_URL = 'https://api.telegram.org/bot' .. config.bot_api_key .. '/'
-- Fetch bot information. Try until it succeeds.
repeat
@ -35,10 +33,10 @@ function bot:init() -- The function run when the bot is started or reloaded.
self.database.users[tostring(self.info.id)] = self.info
self.plugins = {} -- Load plugins.
for _,v in ipairs(self.config.plugins) do
for _,v in ipairs(config.plugins) do
local p = require('plugins.'..v)
table.insert(self.plugins, p)
if p.init then p.init(self) end
if p.init then p.init(self, config) end
end
print('@' .. self.info.username .. ', AKA ' .. self.info.first_name ..' ('..self.info.id..')')
@ -49,7 +47,7 @@ function bot:init() -- The function run when the bot is started or reloaded.
end
function bot:on_msg_receive(msg) -- The fn run whenever a message is received.
function bot:on_msg_receive(msg, config) -- The fn run whenever a message is received.
-- Cache user info for those involved.
utilities.create_user_entry(self, msg.from)
@ -72,11 +70,11 @@ function bot:on_msg_receive(msg) -- The fn run whenever a message is received.
for _,w in pairs(v.triggers) do
if string.match(msg.text:lower(), w) then
local success, result = pcall(function()
return v.action(self, msg)
return v.action(self, msg, config)
end)
if not success then
utilities.send_reply(self, msg, 'Sorry, an unexpected error occurred.')
utilities.handle_exception(self, result, msg.from.id .. ': ' .. msg.text)
utilities.handle_exception(self, result, msg.from.id .. ': ' .. msg.text, config)
return
end
-- If the action returns a table, make that table the new msg.
@ -93,7 +91,8 @@ function bot:on_msg_receive(msg) -- The fn run whenever a message is received.
end
function bot:run()
bot.init(self) -- Actually start the script. Run the bot_init function.
local config = require('config') -- Load configuration file.
bot.init(self, config) -- Actually start the script.
while self.is_started do -- Start a loop while the bot should be running.
@ -102,7 +101,7 @@ function bot:run()
for _,v in ipairs(res.result) do -- Go through every new message.
self.last_update = v.update_id
if v.message then
bot.on_msg_receive(self, v.message)
bot.on_msg_receive(self, v.message, config)
end
end
else
@ -116,7 +115,7 @@ function bot:run()
if v.cron then -- Call each plugin's cron function, if it has one.
local result, err = pcall(function() v.cron(self) end)
if not result then
utilities.handle_exception(self, err, 'CRON: ' .. i)
utilities.handle_exception(self, err, 'CRON: ' .. i, config)
end
end
end

View File

@ -10,13 +10,13 @@ about.triggers = {
''
}
function about:action(msg)
function about:action(msg, config)
-- Filthy hack, but here is where we'll stop forwarded messages from hitting
-- other plugins.
if msg.forward_from then return end
local output = self.config.about_text .. '\nBased on otouto v'..bot.version..' by topkecleon.'
local output = config.about_text .. '\nBased on otouto v'..bot.version..' by topkecleon.'
if (msg.new_chat_participant and msg.new_chat_participant.id == self.info.id)
or msg.text_lower:match('^/about')

View File

@ -48,7 +48,7 @@ local utilities = require('utilities')
local administration = {}
function administration:init()
function administration:init(config)
-- Build the administration db if nonexistent.
if not self.database.administration then
self.database.administration = {
@ -64,9 +64,9 @@ function administration:init()
flood = {}
}
drua.PORT = self.config.cli_port or 4567
drua.PORT = config.cli_port or 4567
administration.init_command(self)
administration.init_command(self, config)
end
@ -139,13 +139,13 @@ administration.ranks = {
[5] = 'Owner'
}
function administration:get_rank(target, chat)
function administration:get_rank(target, chat, config)
target = tostring(target)
chat = tostring(chat)
-- Return 5 if the target is the bot or its owner.
if tonumber(target) == self.config.admin or tonumber(target) == self.info.id then
if tonumber(target) == config.admin or tonumber(target) == self.info.id then
return 5
end
@ -180,10 +180,10 @@ function administration:get_rank(target, chat)
end
function administration:get_target(msg)
function administration:get_target(msg, config)
local target = utilities.user_from_message(self, msg)
if target.id then
target.rank = administration.get_rank(self, target.id_str, msg.chat.id)
target.rank = administration.get_rank(self, target.id_str, msg.chat.id, config)
end
return target
end
@ -255,7 +255,7 @@ function administration:update_desc(chat)
drua.channel_set_about(chat, desc)
end
function administration:kick_user(chat, target, reason)
function administration:kick_user(chat, target, reason, config)
drua.kick_user(chat, target)
local victim = target
if self.database.users[tostring(target)] then
@ -265,10 +265,10 @@ function administration:kick_user(chat, target, reason)
)
end
local group = self.database.administration.groups[tostring(chat)].name
utilities.handle_exception(self, victim..' kicked from '..group, reason)
utilities.handle_exception(self, victim..' kicked from '..group, reason, config)
end
function administration.init_command(self_)
function administration.init_command(self_, config)
administration.commands = {
{ -- generic, mostly autokicks
@ -277,9 +277,10 @@ function administration.init_command(self_)
privilege = 0,
interior = true,
action = function(self, msg, group)
action = function(self, msg, group, config)
local rank = administration.get_rank(self, msg.from.id, msg.chat.id)
local rank = administration.get_rank(self, msg.from.id, msg.chat.id, config)
local user = {}
if rank < 2 then
@ -360,7 +361,7 @@ function administration.init_command(self_)
-- the original guy.
if msg.new_chat_participant.id ~= msg.from.id then
new_user = {}
new_rank = administration.get_rank(self,noob.id, msg.chat.id)
new_rank = administration.get_rank(self,noob.id, msg.chat.id, config)
end
if new_rank == 0 then
@ -425,7 +426,7 @@ function administration.init_command(self_)
end
if new_user ~= user and new_user.do_kick then
administration.kick_user(self, msg.chat.id, msg.new_chat_participant.id, new_user.reason)
administration.kick_user(self, msg.chat.id, msg.new_chat_participant.id, new_user.reason, config)
if new_user.output then
utilities.send_message(self, msg.new_chat_participant.id, new_user.output)
end
@ -450,7 +451,7 @@ function administration.init_command(self_)
end
if user.do_kick then
administration.kick_user(self, msg.chat.id, msg.from.id, user.reason)
administration.kick_user(self, msg.chat.id, msg.from.id, user.reason, config)
if user.output then
utilities.send_message(self, msg.from.id, user.output)
end
@ -487,7 +488,7 @@ function administration.init_command(self_)
interior = false,
doc = 'Returns a list of administrated groups.',
action = function(self, msg)
action = function(self, msg, group, config)
local output = ''
for _,v in ipairs(self.database.administration.activity) do
local group = self.database.administration.groups[v]
@ -516,8 +517,8 @@ function administration.init_command(self_)
interior = false,
doc = 'Returns a list of realm-related commands for your rank (in a private message), or command-specific help.',
action = function(self, msg)
local rank = administration.get_rank(self, msg.from.id, msg.chat.id)
action = function(self, msg, group, config)
local rank = administration.get_rank(self, msg.from.id, msg.chat.id, config)
local input = utilities.get_word(msg.text_lower, 2)
if input then
input = input:gsub('^/', '')
@ -562,7 +563,7 @@ function administration.init_command(self_)
interior = true,
doc = 'Returns a list of moderators and the governor for the group.',
action = function(self, msg, group)
action = function(self, msg, group, config)
local modstring = ''
for k,_ in pairs(group.mods) do
modstring = modstring .. administration.mod_format(self, k)
@ -592,7 +593,7 @@ function administration.init_command(self_)
interior = true,
doc = 'Returns a description of the group (in a private message), including its motd, rules, flags, governor, and moderators.',
action = function(self, msg)
action = function(self, msg, group, config)
local output = administration.get_desc(self, msg.chat.id)
if utilities.send_message(self, msg.from.id, output, true, nil, true) then
if msg.from.id ~= msg.chat.id then
@ -612,7 +613,7 @@ function administration.init_command(self_)
interior = true,
doc = 'Returns the group\'s list of rules, or a specific rule.',
action = function(self, msg, group)
action = function(self, msg, group, config)
local output
local input = utilities.get_word(msg.text_lower, 2)
input = tonumber(input)
@ -640,7 +641,7 @@ function administration.init_command(self_)
interior = true,
doc = 'Returns the group\'s message of the day.',
action = function(self, msg, group)
action = function(self, msg, group, config)
local output = 'No MOTD has been set for ' .. msg.chat.title .. '.'
if group.motd then
output = '*MOTD for ' .. msg.chat.title .. ':*\n' .. group.motd
@ -657,7 +658,7 @@ function administration.init_command(self_)
interior = true,
doc = 'Returns the group\'s link.',
action = function(self, msg, group)
action = function(self, msg, group, config)
local output = 'No link has been set for ' .. msg.chat.title .. '.'
if group.link then
output = '[' .. msg.chat.title .. '](' .. group.link .. ')'
@ -674,11 +675,11 @@ function administration.init_command(self_)
interior = true,
doc = 'Removes the user from the group.',
action = function(self, msg)
if administration.get_rank(self, msg.from.id) == 5 then
action = function(self, msg, group, config)
if administration.get_rank(self, msg.from.id, nil, config) == 5 then
utilities.send_reply(self, msg, 'I can\'t let you do that, '..msg.from.name..'.')
else
administration.kick_user(self, msg.chat.id, msg.from.id, 'kickme')
administration.kick_user(self, msg.chat.id, msg.from.id, 'kickme', config)
utilities.send_message(self, msg.chat.id, 'Goodbye, ' .. msg.from.name .. '!', true)
if msg.chat.type == 'supergroup' then
bindings.unbanChatMember(self, { chat_id = msg.chat.id, user_id = msg.from.id } )
@ -695,14 +696,14 @@ function administration.init_command(self_)
interior = true,
doc = 'Removes a user from the group. The target may be specified via reply, username, or ID.',
action = function(self, msg)
local target = administration.get_target(self, msg)
action = function(self, msg, group, config)
local target = administration.get_target(self, msg, config)
if target.err then
utilities.send_reply(self, msg, target.err)
elseif target.rank > 1 then
utilities.send_reply(self, msg, target.name .. ' is too privileged to be kicked.')
else
administration.kick_user(self, msg.chat.id, target.id, 'kicked by ' .. msg.from.name)
administration.kick_user(self, msg.chat.id, target.id, 'kicked by ' .. msg.from.name, config)
utilities.send_message(self, msg.chat.id, target.name .. ' has been kicked.')
if msg.chat.type == 'supergroup' then
bindings.unbanChatMember(self, { chat_id = msg.chat.id, user_id = target.id } )
@ -719,8 +720,8 @@ function administration.init_command(self_)
interior = true,
doc = 'Bans a user from the group. The target may be specified via reply, username, or ID.',
action = function(self, msg, group)
local target = administration.get_target(self, msg)
action = function(self, msg, group, config)
local target = administration.get_target(self, msg, config)
if target.err then
utilities.send_reply(self, msg, target.err)
elseif target.rank > 1 then
@ -728,7 +729,7 @@ function administration.init_command(self_)
elseif group.bans[target.id_str] then
utilities.send_reply(self, msg, target.name .. ' is already banned.')
else
administration.kick_user(self, msg.chat.id, target.id, 'banned by '..msg.from.name)
administration.kick_user(self, msg.chat.id, target.id, 'banned by '..msg.from.name, config)
utilities.send_reply(self, msg, target.name .. ' has been banned.')
group.bans[target.id_str] = true
end
@ -743,8 +744,8 @@ function administration.init_command(self_)
interior = true,
doc = 'Unbans a user from the group. The target may be specified via reply, username, or ID.',
action = function(self, msg, group)
local target = administration.get_target(self, msg)
action = function(self, msg, group, config)
local target = administration.get_target(self, msg, config)
if target.err then
utilities.send_reply(self, msg, target.err)
else
@ -769,7 +770,7 @@ function administration.init_command(self_)
interior = true,
doc = 'Sets the group\'s rules. Rules will be automatically numbered. Separate rules with a new line. Markdown is supported. Pass "--" to delete the rules.',
action = function(self, msg, group)
action = function(self, msg, group, config)
local input = msg.text:match('^/setrules[@'..self.info.username..']*(.+)')
if input == ' --' or input == ' ' .. utilities.char.em_dash then
group.rules = {}
@ -799,7 +800,7 @@ function administration.init_command(self_)
interior = true,
doc = 'Changes a single rule. Pass "--" to delete the rule. If i is a number for which there is no rule, adds a rule by the next incremented number.',
action = function(self, msg, group)
action = function(self, msg, group, config)
local input = utilities.input(msg.text)
local output = 'usage: `/changerule <i> <newrule>`'
if input then
@ -836,7 +837,7 @@ function administration.init_command(self_)
interior = true,
doc = 'Sets the group\'s message of the day. Markdown is supported. Pass "--" to delete the message.',
action = function(self, msg, group)
action = function(self, msg, group, config)
local input = utilities.input(msg.text)
if not input and msg.reply_to_message and msg.reply_to_message.text:len() > 0 then
input = msg.reply_to_message.text
@ -868,7 +869,7 @@ function administration.init_command(self_)
interior = true,
doc = 'Sets the group\'s join link. Pass "--" to regenerate the link.',
action = function(self, msg, group)
action = function(self, msg, group, config)
local input = utilities.input(msg.text)
if input == '--' or input == utilities.char.em_dash then
group.link = drua.export_link(msg.chat.id)
@ -891,9 +892,9 @@ function administration.init_command(self_)
interior = true,
doc = 'Returns a list of administrators. Owner is denoted with a star character.',
action = function(self, msg)
action = function(self, msg, config)
local output = '*Administrators:*\n'
output = output .. administration.mod_format(self, self.config.admin):gsub('\n', '\n')
output = output .. administration.mod_format(self, config.admin):gsub('\n', '\n')
for id,_ in pairs(self.database.administration.admins) do
output = output .. administration.mod_format(self, id)
end
@ -909,7 +910,7 @@ function administration.init_command(self_)
interior = true,
doc = 'Returns a list of flags or toggles the specified flag.',
action = function(self, msg, group)
action = function(self, msg, group, config)
local input = utilities.input(msg.text)
if input then
input = utilities.get_word(input, 1)
@ -941,7 +942,7 @@ function administration.init_command(self_)
interior = true,
doc = 'Returns a list of antiflood values or sets one.',
action = function(self, msg, group)
action = function(self, msg, group, config)
if not group.flags[5] then
utilities.send_message(self, msg.chat.id, 'antiflood is not enabled. Use `/flag 5` to enable it.', true, nil, true)
else
@ -981,8 +982,8 @@ function administration.init_command(self_)
interior = true,
doc = 'Promotes a user to a moderator. The target may be specified via reply, username, or ID.',
action = function(self, msg, group)
local target = administration.get_target(self, msg)
action = function(self, msg, group, config)
local target = administration.get_target(self, msg, config)
if target.err then
utilities.send_reply(self, msg, target.err)
else
@ -1008,8 +1009,8 @@ function administration.init_command(self_)
interior = true,
doc = 'Demotes a moderator to a user. The target may be specified via reply, username, or ID.',
action = function(self, msg, group)
local target = administration.get_target(self, msg)
action = function(self, msg, group, config)
local target = administration.get_target(self, msg, config)
if target.err then
utilities.send_reply(self, msg, target.err)
else
@ -1034,8 +1035,8 @@ function administration.init_command(self_)
interior = true,
doc = 'Promotes a user to the governor. The current governor will be replaced. The target may be specified via reply, username, or ID.',
action = function(self, msg, group)
local target = administration.get_target(self, msg)
action = function(self, msg, group, config)
local target = administration.get_target(self, msg, config)
if target.err then
utilities.send_reply(self, msg, target.err)
else
@ -1063,8 +1064,8 @@ function administration.init_command(self_)
interior = true,
doc = 'Demotes the governor to a user. The administrator will become the new governor. The target may be specified via reply, username, or ID.',
action = function(self, msg, group)
local target = administration.get_target(self, msg)
action = function(self, msg, group, config)
local target = administration.get_target(self, msg, config)
if target.err then
utilities.send_reply(self, msg, target.err)
else
@ -1090,8 +1091,8 @@ function administration.init_command(self_)
interior = false,
doc = 'Bans a user from all groups. The target may be specified via reply, username, or ID.',
action = function(self, msg, group)
local target = administration.get_target(self, msg)
action = function(self, msg, group, config)
local target = administration.get_target(self, msg, config)
if target.err then
utilities.send_reply(self, msg, target.err)
elseif target.rank > 3 then
@ -1099,7 +1100,7 @@ function administration.init_command(self_)
elseif self.database.blacklist[target.id_str] then
utilities.send_reply(self, msg, target.name .. ' is already globally banned.')
else
administration.kick_user(self, msg.chat.id, target.id, 'hammered by '..msg.from.name)
administration.kick_user(self, msg.chat.id, target.id, 'hammered by '..msg.from.name, config)
self.database.blacklist[target.id_str] = true
for k,v in pairs(self.database.administration.groups) do
if not v.flags[6] then
@ -1126,8 +1127,8 @@ function administration.init_command(self_)
interior = false,
doc = 'Removes a global ban. The target may be specified via reply, username, or ID.',
action = function(self, msg)
local target = administration.get_target(self, msg)
action = function(self, msg, group, config)
local target = administration.get_target(self, msg, config)
if target.err then
utilities.send_reply(self, msg, target.err)
elseif not self.database.blacklist[target.id_str] then
@ -1150,8 +1151,8 @@ function administration.init_command(self_)
interior = false,
doc = 'Promotes a user to an administrator. The target may be specified via reply, username, or ID.',
action = function(self, msg, group)
local target = administration.get_target(self, msg)
action = function(self, msg, group, config)
local target = administration.get_target(self, msg, config)
if target.err then
utilities.send_reply(self, msg, target.err)
elseif target.rank >= 4 then
@ -1177,8 +1178,8 @@ function administration.init_command(self_)
interior = false,
doc = 'Demotes an administrator to a user. The target may be specified via reply, username, or ID.',
action = function(self, msg)
local target = administration.get_target(self, msg)
action = function(self, msg, group, config)
local target = administration.get_target(self, msg, config)
if target.err then
utilities.send_reply(self, msg, target.err)
else
@ -1205,7 +1206,7 @@ function administration.init_command(self_)
interior = false,
doc = 'Adds a group to the administration system. Pass numbers as arguments to enable those flags immediately. For example, this would add the group and enable the unlisted flag, antibot, and antiflood:\n/gadd 1 4 5',
action = function(self, msg)
action = function(self, msg, group, config)
if self.database.administration.groups[msg.chat.id_str] then
utilities.send_reply(self, msg, 'I am already administrating this group.')
else
@ -1253,7 +1254,7 @@ function administration.init_command(self_)
interior = false,
doc = 'Removes a group from the administration system.',
action = function(self, msg)
action = function(self, msg, group, config)
local input = utilities.input(msg.text) or msg.chat.id_str
local output
if self.database.administration.groups[input] then
@ -1284,7 +1285,7 @@ function administration.init_command(self_)
interior = false,
doc = 'Returns a list (in a private message) of all administrated groups with their governors and links.',
action = function(self, msg)
action = function(self, msg, group, config)
local output = ''
if utilities.table_size(self.database.administration.groups) > 0 then
for k,v in pairs(self.database.administration.groups) do
@ -1313,7 +1314,7 @@ function administration.init_command(self_)
interior = false,
doc = 'Broadcasts a message to all administrated groups.',
action = function(self, msg)
action = function(self, msg, group, config)
local input = utilities.input(msg.text)
if not input then
utilities.send_reply(self, msg, 'Give me something to broadcast.')
@ -1357,17 +1358,17 @@ function administration.init_command(self_)
end
end
function administration:action(msg)
function administration:action(msg, config)
for _,command in ipairs(administration.commands) do
for _,trigger in pairs(command.triggers) do
if msg.text_lower:match(trigger) then
if command.interior and not self.database.administration.groups[msg.chat.id_str] then
break
end
if administration.get_rank(self, msg.from.id, msg.chat.id) < command.privilege then
if administration.get_rank(self, msg.from.id, msg.chat.id, config) < command.privilege then
break
end
local res = command.action(self, msg, self.database.administration.groups[msg.chat.id_str])
local res = command.action(self, msg, self.database.administration.groups[msg.chat.id_str], config)
if res ~= true then
return res
end

View File

@ -24,17 +24,17 @@ function apod:init()
:t('apod', true):t('apodhd', true):t('apodtext', true).table
end
function apod:action(msg)
function apod:action(msg, config)
if not self.config.nasa_api_key then
self.config.nasa_api_key = 'DEMO_KEY'
if not config.nasa_api_key then
config.nasa_api_key = 'DEMO_KEY'
end
local input = utilities.input(msg.text)
local date = '*'
local disable_page_preview = false
local url = 'https://api.nasa.gov/planetary/apod?api_key=' .. self.config.nasa_api_key
local url = 'https://api.nasa.gov/planetary/apod?api_key=' .. config.nasa_api_key
if input then
if input:match('(%d+)%-(%d+)%-(%d+)$') then
@ -52,14 +52,14 @@ function apod:action(msg)
local jstr, res = HTTPS.request(url)
if res ~= 200 then
utilities.send_reply(self, msg, self.config.errors.connection)
utilities.send_reply(self, msg, config.errors.connection)
return
end
local jdat = JSON.decode(jstr)
if jdat.error then
utilities.send_reply(msg, self.config.errors.results)
utilities.send_reply(self, msg, config.errors.results)
return
end

View File

@ -4,8 +4,8 @@ local HTTP = require('socket.http')
local URL = require('socket.url')
local utilities = require('utilities')
function bible:init()
if not self.config.biblia_api_key then
function bible:init(config)
if not config.biblia_api_key then
print('Missing config value: biblia_api_key.')
print('bible.lua will not be enabled.')
return
@ -21,7 +21,7 @@ Returns a verse from the American Standard Version of the Bible, or an apocrypha
Alias: /b
```]]
function bible:action(msg)
function bible:action(msg, config)
local input = utilities.input(msg.text)
if not input then
@ -29,17 +29,17 @@ function bible:action(msg)
return
end
local url = 'http://api.biblia.com/v1/bible/content/ASV.txt?key=' .. self.config.biblia_api_key .. '&passage=' .. URL.escape(input)
local url = 'http://api.biblia.com/v1/bible/content/ASV.txt?key=' .. config.biblia_api_key .. '&passage=' .. URL.escape(input)
local output, res = HTTP.request(url)
if not output or res ~= 200 or output:len() == 0 then
url = 'http://api.biblia.com/v1/bible/content/KJVAPOC.txt?key=' .. self.config.biblia_api_key .. '&passage=' .. URL.escape(input)
url = 'http://api.biblia.com/v1/bible/content/KJVAPOC.txt?key=' .. config.biblia_api_key .. '&passage=' .. URL.escape(input)
output, res = HTTP.request(url)
end
if not output or res ~= 200 or output:len() == 0 then
output = self.config.errors.results
output = config.errors.results
end
if output:len() > 4000 then

View File

@ -15,12 +15,12 @@ blacklist.triggers = {
''
}
function blacklist:action(msg)
function blacklist:action(msg, config)
if self.database.blacklist[msg.from.id_str] then return end
if self.database.blacklist[msg.chat.id_str] then return end
if not msg.text:match('^/blacklist') then return true end
if msg.from.id ~= self.config.admin then return end
if msg.from.id ~= config.admin then return end
local target = utilities.user_from_message(self, msg)
if target.err then

View File

@ -14,7 +14,7 @@ function calc:init()
calc.triggers = utilities.triggers(self.info.username):t('calc', true).table
end
function calc:action(msg)
function calc:action(msg, config)
local input = utilities.input(msg.text)
if not input then
@ -30,7 +30,7 @@ function calc:action(msg)
local output = HTTPS.request(url)
if not output then
utilities.send_reply(self, msg, self.config.errors.connection)
utilities.send_reply(self, msg, config.errors.connection)
return
end

View File

@ -3,8 +3,8 @@ local cats = {}
local HTTP = require('socket.http')
local utilities = require('utilities')
function cats:init()
if not self.config.thecatapi_key then
function cats:init(config)
if not config.thecatapi_key then
print('Missing config value: thecatapi_key.')
print('cats.lua will be enabled, but there are more features with a key.')
end
@ -15,16 +15,16 @@ end
cats.command = 'cat'
cats.doc = '`Returns a cat!`'
function cats:action(msg)
function cats:action(msg, config)
local url = 'http://thecatapi.com/api/images/get?format=html&type=jpg'
if self.config.thecatapi_key then
url = url .. '&api_key=' .. self.config.thecatapi_key
if config.thecatapi_key then
url = url .. '&api_key=' .. config.thecatapi_key
end
local str, res = HTTP.request(url)
if res ~= 200 then
utilities.send_reply(msg, self.config.errors.connection)
utilities.send_reply(self, msg, onfig.errors.connection)
return
end

View File

@ -8,8 +8,8 @@ local JSON = require('dkjson')
local bindings = require('bindings')
local utilities = require('utilities')
function chatter:init()
if not self.config.simsimi_key then
function chatter:init(config)
if not config.simsimi_key then
print('Missing config value: simsimi_key.')
print('chatter.lua will not be enabled.')
return
@ -22,7 +22,7 @@ end
chatter.base_url = 'http://%sapi.simsimi.com/request.p?key=%s&lc=%s&ft=1.0&text=%s'
function chatter:action(msg)
function chatter:action(msg, config)
if msg.text == '' then return true end
@ -47,17 +47,17 @@ function chatter:action(msg)
local sandbox = self.config.simsimi_trial and 'sandbox.' or ''
local url = chatter.base_url:format(sandbox, self.config.simsimi_key, self.config.lang, URL.escape(input))
local url = chatter.base_url:format(sandbox, config.simsimi_key, self.config.lang, URL.escape(input))
local jstr, res = HTTP.request(url)
if res ~= 200 then
utilities.send_message(self, msg.chat.id, self.config.errors.chatter_connection)
utilities.send_message(self, msg.chat.id, config.errors.chatter_connection)
return
end
local jdat = JSON.decode(jstr)
if not jdat.response or jdat.response:match('^I HAVE NO RESPONSE.') then
utilities.send_message(self, msg.chat.id, self.config.errors.chatter_response)
utilities.send_message(self, msg.chat.id, config.errors.chatter_response)
return
end
local output = jdat.response

View File

@ -8,9 +8,9 @@ function control:init()
table.insert(control.triggers, '^/script')
end
function control:action(msg)
function control:action(msg, config)
if msg.from.id ~= self.config.admin then
if msg.from.id ~= config.admin then
return
end
@ -21,11 +21,14 @@ function control:action(msg)
if pac:match('^plugins%.') then
package.loaded[pac] = nil
end
end
package.loaded['bindings'] = nil
package.loaded['utilities'] = nil
package.loaded['config'] = nil
for k, v in pairs(require('config')) do
config[k] = v
end
bot.init(self)
bot.init(self, config)
utilities.send_reply(self, msg, 'Bot reloaded!')
elseif msg.text:match('^'..utilities.INVOCATION_PATTERN..'halt') then
self.is_started = false

View File

@ -15,7 +15,7 @@ function currency:init()
currency.triggers = utilities.triggers(self.info.username):t('cash', true).table
end
function currency:action(msg)
function currency:action(msg, config)
local input = msg.text:upper()
if not input:match('%a%a%a TO %a%a%a') then
@ -36,13 +36,13 @@ function currency:action(msg)
url = url .. '?from=' .. from .. '&to=' .. to .. '&a=' .. amount
local str, res = HTTPS.request(url)
if res ~= 200 then
utilities.send_reply(self, msg, self.config.errors.connection)
utilities.send_reply(self, msg, config.errors.connection)
return
end
str = str:match('<span class=bld>(.*) %u+</span>')
if not str then
utilities.send_reply(self, msg, self.config.errors.results)
utilities.send_reply(self, msg, config.errors.results)
return
end

View File

@ -17,7 +17,7 @@ function dilbert:init()
dilbert.triggers = utilities.triggers(self.info.username):t('dilbert', true).table
end
function dilbert:action(msg)
function dilbert:action(msg, config)
bindings.sendChatAction(self, { chat_id = msg.chat.id, action = 'upload_photo' } )
@ -28,7 +28,7 @@ function dilbert:action(msg)
local url = 'http://dilbert.com/strip/' .. URL.escape(input)
local str, res = HTTP.request(url)
if res ~= 200 then
utilities.send_reply(self, msg, self.config.errors.connection)
utilities.send_reply(self, msg, config.errors.connection)
return
end

View File

@ -8,12 +8,12 @@ local URL = require('socket.url')
local JSON = require('dkjson')
local utilities = require('utilities')
function gImages:init()
if not self.config.google_api_key then
function gImages:init(config)
if not config.google_api_key then
print('Missing config value: google_api_key.')
print('gImages.lua will not be enabled.')
return
elseif not self.config.google_cse_key then
elseif not config.google_cse_key then
print('Missing config value: google_cse_key.')
print('gImages.lua will not be enabled.')
return
@ -29,7 +29,7 @@ Returns a randomized top result from Google Images. Safe search is enabled by de
Alias: /i
```]]
function gImages:action(msg)
function gImages:action(msg, config)
local input = utilities.input(msg.text)
if not input then
@ -41,7 +41,7 @@ function gImages:action(msg)
end
end
local url = 'https://www.googleapis.com/customsearch/v1?&searchType=image&imgSize=xlarge&alt=json&num=8&start=1&key=' .. self.config.google_api_key .. '&cx=' .. self.config.google_cse_key
local url = 'https://www.googleapis.com/customsearch/v1?&searchType=image&imgSize=xlarge&alt=json&num=8&start=1&key=' .. config.google_api_key .. '&cx=' .. config.google_cse_key
if not string.match(msg.text, '^/i[mage]*nsfw') then
url = url .. '&safe=high'
@ -51,13 +51,13 @@ function gImages:action(msg)
local jstr, res = HTTPS.request(url)
if res ~= 200 then
utilities.send_reply(self, msg, self.config.errors.connection)
utilities.send_reply(self, msg, config.errors.connection)
return
end
local jdat = JSON.decode(jstr)
if jdat.searchInformation.totalResults == '0' then
utilities.send_reply(self, msg, self.config.errors.results)
utilities.send_reply(self, msg, config.errors.results)
return
end

View File

@ -14,7 +14,7 @@ function gMaps:init()
gMaps.triggers = utilities.triggers(self.info.username):t('location', true):t('loc', true).table
end
function gMaps:action(msg)
function gMaps:action(msg, config)
local input = utilities.input(msg.text)
if not input then
@ -26,7 +26,7 @@ function gMaps:action(msg)
end
end
local coords = utilities.get_coords(self, input)
local coords = utilities.get_coords(self, input, config)
if type(coords) == 'string' then
utilities.send_reply(self, msg, coords)
return

View File

@ -16,7 +16,7 @@ function gSearch:init()
gSearch.triggers = utilities.triggers(self.info.username):t('g', true):t('google', true):t('gnsfw', true).table
end
function gSearch:action(msg)
function gSearch:action(msg, config)
local input = utilities.input(msg.text)
if not input then
@ -44,17 +44,17 @@ function gSearch:action(msg)
local jstr, res = HTTPS.request(url)
if res ~= 200 then
utilities.send_reply(self, msg, self.config.errors.connection)
utilities.send_reply(self, msg, config.errors.connection)
return
end
local jdat = JSON.decode(jstr)
if not jdat.responseData then
utilities.send_reply(self, msg, self.config.errors.connection)
utilities.send_reply(self, msg, config.errors.connection)
return
end
if not jdat.responseData.results[1] then
utilities.send_reply(self, msg, self.config.errors.results)
utilities.send_reply(self, msg, config.errors.results)
return
end

View File

@ -6,9 +6,8 @@ local greetings = {}
local utilities = require('utilities')
function greetings:init()
if not self.config.greetings then
self.config.greetings = {
function greetings:init(config)
config.greetings = config.greetings or {
['Hello, #NAME.'] = {
'hello',
'hey',
@ -34,18 +33,17 @@ function greetings:init()
'thank you'
}
}
end
greetings.triggers = {
self.info.first_name:lower() .. '%p*$'
}
end
function greetings:action(msg)
function greetings:action(msg, config)
local nick = self.database.users[msg.from.id_str].nickname or msg.from.first_name
for trigger,responses in pairs(self.config.greetings) do
for trigger,responses in pairs(config.greetings) do
for _,response in pairs(responses) do
if msg.text_lower:match(response..',? '..self.info.first_name:lower()) then
utilities.send_message(self, msg.chat.id, utilities.latcyr(trigger:gsub('#NAME', nick)))

View File

@ -15,13 +15,13 @@ function hackernews:init()
hackernews.triggers = utilities.triggers(self.info.username):t('hackernews', true):t('hn', true).table
end
function hackernews:action(msg)
function hackernews:action(msg, config)
bindings.sendChatAction(self, { chat_id = msg.chat.id, action = 'typing' } )
local jstr, res = HTTPS.request('https://hacker-news.firebaseio.com/v0/topstories.json')
if res ~= 200 then
utilities.send_reply(self, msg, self.config.errors.connection)
utilities.send_reply(self, msg, config.errors.connection)
return
end
@ -37,7 +37,7 @@ function hackernews:action(msg)
local res_url = 'https://hacker-news.firebaseio.com/v0/item/' .. jdat[i] .. '.json'
jstr, res = HTTPS.request(res_url)
if res ~= 200 then
utilities.send_reply(self, msg, self.config.errors.connection)
utilities.send_reply(self, msg, config.errors.connection)
return
end
local res_jdat = JSON.decode(jstr)
@ -47,7 +47,7 @@ function hackernews:action(msg)
end
local url = res_jdat.url
if not url then
utilities.send_reply(self, msg, self.config.errors.connection)
utilities.send_reply(self, msg, config.errors.connection)
return
end
if url:find('%(') then

View File

@ -102,7 +102,7 @@ local function format_card(card)
end
function hearthstone:action(msg)
function hearthstone:action(msg, config)
local input = utilities.input(msg.text_lower)
if not input then
@ -119,7 +119,7 @@ function hearthstone:action(msg)
output = utilities.trim(output)
if output:len() == 0 then
utilities.send_reply(self, msg, self.config.errors.results)
utilities.send_reply(self, msg, config.errors.results)
return
end

View File

@ -15,7 +15,7 @@ function imdb:init()
imdb.triggers = utilities.triggers(self.info.username):t('imdb', true).table
end
function imdb:action(msg)
function imdb:action(msg, config)
local input = utilities.input(msg.text)
if not input then
@ -31,14 +31,14 @@ function imdb:action(msg)
local jstr, res = HTTP.request(url)
if res ~= 200 then
utilities.send_reply(self, msg, self.config.errors.connection)
utilities.send_reply(self, msg, config.errors.connection)
return
end
local jdat = JSON.decode(jstr)
if jdat.Response ~= 'True' then
utilities.send_reply(self, msg, self.config.errors.results)
utilities.send_reply(self, msg, config.errors.results)
return
end

View File

@ -8,8 +8,8 @@ local URL = require('socket.url')
local JSON = require('dkjson')
local utilities = require('utilities')
function lastfm:init()
if not self.config.lastfm_api_key then
function lastfm:init(config)
if not config.lastfm_api_key then
print('Missing config value: lastfm_api_key.')
print('lastfm.lua will not be enabled.')
return
@ -27,7 +27,7 @@ Returns what you are or were last listening to. If you specify a username, info
Sets your last.fm username. Otherwise, /np will use your Telegram username. Use "/fmset --" to delete it.
```]]
function lastfm:action(msg)
function lastfm:action(msg, config)
local input = utilities.input(msg.text)
@ -47,7 +47,7 @@ function lastfm:action(msg)
return
end
local url = 'http://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&format=json&limit=1&api_key=' .. self.config.lastfm_api_key .. '&user='
local url = 'http://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&format=json&limit=1&api_key=' .. config.lastfm_api_key .. '&user='
local username
local alert = ''
@ -72,7 +72,7 @@ function lastfm:action(msg)
jstr, res = HTTP.request(url)
end)
if res ~= 200 then
utilities.send_reply(self, msg, self.config.errors.connection)
utilities.send_reply(self, msg, config.errors.connection)
return
end

View File

@ -8,9 +8,9 @@ function luarun:init()
luarun.triggers = utilities.triggers(self.info.username):t('lua', true):t('return', true).table
end
function luarun:action(msg)
function luarun:action(msg, config)
if msg.from.id ~= self.config.admin then
if msg.from.id ~= config.admin then
return true
end

View File

@ -6,11 +6,11 @@ function me:init()
me.triggers = utilities.triggers(self.info.username):t('me', true).table
end
function me:action(msg)
function me:action(msg, config)
local target = self.database.users[msg.from.id_str]
if msg.from.id == self.config.admin and (msg.reply_to_message or utilities.input(msg.text)) then
if msg.from.id == config.admin and (msg.reply_to_message or utilities.input(msg.text)) then
target = utilities.user_from_message(self, msg, true)
if target.err then
utilities.send_reply(self, msg, target.err)

View File

@ -12,11 +12,11 @@ function nick:init()
nick.triggers = utilities.triggers(self.info.username):t('nick', true).table
end
function nick:action(msg)
function nick:action(msg, config)
local target = msg.from
if msg.from.id == self.config.admin and msg.reply_to_message then
if msg.from.id == config.admin and msg.reply_to_message then
target = msg.reply_to_message.from
target.id_str = tostring(target.id)
target.name = target.first_name

View File

@ -16,7 +16,7 @@ function pokedex:init()
pokedex.triggers = utilities.triggers(self.info.username):t('pokedex', true):t('dex', true).table
end
function pokedex:action(msg)
function pokedex:action(msg, config)
bindings.sendChatAction(self, { chat_id = msg.chat.id, action = 'typing' } )
@ -35,7 +35,7 @@ function pokedex:action(msg)
local dex_url = url .. '/api/v1/pokemon/' .. input
local dex_jstr, res = HTTP.request(dex_url)
if res ~= 200 then
utilities.send_reply(self, msg, self.config.errors.connection)
utilities.send_reply(self, msg, config.errors.connection)
return
end
@ -44,7 +44,7 @@ function pokedex:action(msg)
local desc_url = url .. dex_jdat.descriptions[math.random(#dex_jdat.descriptions)].resource_uri
local desc_jstr, _ = HTTP.request(desc_url)
if res ~= 200 then
utilities.send_reply(self, msg, self.config.errors.connection)
utilities.send_reply(self, msg, config.errors.connection)
return
end

View File

@ -39,7 +39,7 @@ reddit.subreddit_url = 'http://www.reddit.com/%s/.json?limit='
reddit.search_url = 'http://www.reddit.com/search.json?q=%s&limit='
reddit.rall_url = 'http://www.reddit.com/.json?limit='
function reddit:action(msg)
function reddit:action(msg, config)
-- Eight results in PM, four results elsewhere.
local limit = 4
if msg.chat.type == 'private' then
@ -69,11 +69,11 @@ function reddit:action(msg)
end
local jstr, res = HTTP.request(url)
if res ~= 200 then
utilities.send_reply(self, msg, self.config.errors.connection)
utilities.send_reply(self, msg, config.errors.connection)
else
local jdat = JSON.decode(jstr)
if #jdat.data.children == 0 then
utilities.send_reply(self, msg, self.config.errors.results)
utilities.send_reply(self, msg, config.errors.results)
else
local output = format_results(jdat.data.children)
output = source .. output

View File

@ -6,9 +6,9 @@ function shell:init()
shell.triggers = utilities.triggers(self.info.username):t('run', true).table
end
function shell:action(msg)
function shell:action(msg, config)
if msg.from.id ~= self.config.admin then
if msg.from.id ~= config.admin then
return
end

View File

@ -14,7 +14,7 @@ function time:init()
time.triggers = utilities.triggers(self.info.username):t('time', true).table
end
function time:action(msg)
function time:action(msg, config)
local input = utilities.input(msg.text)
if not input then
@ -26,7 +26,7 @@ function time:action(msg)
end
end
local coords = utilities.get_coords(self, input)
local coords = utilities.get_coords(self, input, config)
if type(coords) == 'string' then
utilities.send_reply(self, msg, coords)
return
@ -39,7 +39,7 @@ function time:action(msg)
local jstr, res = HTTPS.request(url)
if res ~= 200 then
utilities.send_reply(self, msg, self.config.errors.connection)
utilities.send_reply(self, msg, config.errors.connection)
return
end

View File

@ -15,7 +15,7 @@ function translate:init()
translate.triggers = utilities.triggers(self.info.username):t('translate', true):t('tl', true).table
end
function translate:action(msg)
function translate:action(msg, config)
local input = utilities.input(msg.text)
if not input then
@ -27,17 +27,17 @@ function translate:action(msg)
end
end
local url = 'https://translate.yandex.net/api/v1.5/tr.json/translate?key=' .. self.config.yandex_key .. '&lang=' .. self.config.lang .. '&text=' .. URL.escape(input)
local url = 'https://translate.yandex.net/api/v1.5/tr.json/translate?key=' .. config.yandex_key .. '&lang=' .. config.lang .. '&text=' .. URL.escape(input)
local str, res = HTTPS.request(url)
if res ~= 200 then
utilities.send_reply(self, msg, self.config.errors.connection)
utilities.send_reply(self, msg, config.errors.connection)
return
end
local jdat = JSON.decode(str)
if jdat.code ~= 200 then
utilities.send_reply(self, msg, self.config.errors.connection)
utilities.send_reply(self, msg, config.errors.connection)
return
end

View File

@ -16,7 +16,7 @@ function urbandictionary:init()
urbandictionary.triggers = utilities.triggers(self.info.username):t('urbandictionary', true):t('ud', true):t('urban', true).table
end
function urbandictionary:action(msg)
function urbandictionary:action(msg, config)
local input = utilities.input(msg.text)
if not input then
@ -32,13 +32,13 @@ function urbandictionary:action(msg)
local jstr, res = HTTP.request(url)
if res ~= 200 then
utilities.send_reply(self, msg, self.config.errors.connection)
utilities.send_reply(self, msg, config.errors.connection)
return
end
local jdat = JSON.decode(jstr)
if jdat.result_type == "no_results" then
utilities.send_reply(self, msg, self.config.errors.results)
utilities.send_reply(self, msg, config.errors.results)
return
end

View File

@ -4,8 +4,8 @@ local HTTP = require('socket.http')
local JSON = require('dkjson')
local utilities = require('utilities')
function weather:init()
if not self.config.owm_api_key then
function weather:init(config)
if not config.owm_api_key then
print('Missing config value: owm_api_key.')
print('weather.lua will not be enabled.')
return
@ -20,7 +20,7 @@ weather.doc = [[```
Returns the current weather conditions for a given location.
```]]
function weather:action(msg)
function weather:action(msg, config)
local input = utilities.input(msg.text)
if not input then
@ -32,17 +32,17 @@ function weather:action(msg)
end
end
local coords = utilities.get_coords(self, input)
local coords = utilities.get_coords(self, input, config)
if type(coords) == 'string' then
utilities.send_reply(self, msg, coords)
return
end
local url = 'http://api.openweathermap.org/data/2.5/weather?APPID=' .. self.config.owm_api_key .. '&lat=' .. coords.lat .. '&lon=' .. coords.lon
local url = 'http://api.openweathermap.org/data/2.5/weather?APPID=' .. config.owm_api_key .. '&lat=' .. coords.lat .. '&lon=' .. coords.lon
local jstr, res = HTTP.request(url)
if res ~= 200 then
utilities.send_reply(self, msg, self.config.errors.connection)
utilities.send_reply(self, msg, config.errors.connection)
return
end

View File

@ -25,7 +25,7 @@ local get_title = function(search)
return false
end
function wikipedia:action(msg)
function wikipedia:action(msg, config)
-- Get the query. If it's not in the message, check the replied-to message.
-- If those don't exist, send the help text.
@ -50,19 +50,19 @@ function wikipedia:action(msg)
jstr, res = HTTPS.request(search_url .. URL.escape(input))
if res ~= 200 then
utilities.send_reply(self, msg, self.config.errors.connection)
utilities.send_reply(self, msg, config.errors.connection)
return
end
jdat = JSON.decode(jstr)
if jdat.query.searchinfo.totalhits == 0 then
utilities.send_reply(self, msg, self.config.errors.results)
utilities.send_reply(self, msg, config.errors.results)
return
end
local title = get_title(jdat.query.search)
if not title then
utilities.send_reply(self, msg, self.config.errors.results)
utilities.send_reply(self, msg, config.errors.results)
return
end
@ -70,7 +70,7 @@ function wikipedia:action(msg)
jstr, res = HTTPS.request(res_url .. URL.escape(title))
if res ~= 200 then
utilities.send_reply(self, msg, self.config.errors.connection)
utilities.send_reply(self, msg, config.errors.connection)
return
end
@ -78,7 +78,7 @@ function wikipedia:action(msg)
local text = JSON.decode(jstr).query.pages
_, text = next(text)
if not text then
utilities.send_reply(self, msg, self.config.errors.results)
utilities.send_reply(self, msg, config.errors.results)
return
else
text = text.extract

View File

@ -14,11 +14,11 @@ function xkcd:init()
xkcd.triggers = utilities.triggers(self.info.username):t('xkcd', true).table
end
function xkcd:action(msg)
function xkcd:action(msg, config)
local jstr, res = HTTP.request('http://xkcd.com/info.0.json')
if res ~= 200 then
utilities.send_reply(self, msg, self.config.errors.connection)
utilities.send_reply(self, msg, config.errors.connection)
return
end
local latest = JSON.decode(jstr).num
@ -44,7 +44,7 @@ function xkcd:action(msg)
jstr, res = HTTP.request(res_url)
if res ~= 200 then
utilities.send_reply(self, msg, self.config.errors.connection)
utilities.send_reply(self, msg, config.errors.connection)
return
end
local jdat = JSON.decode(jstr)

View File

@ -7,8 +7,8 @@ local URL = require('socket.url')
local JSON = require('dkjson')
local utilities = require('utilities')
function youtube:init()
if not self.config.google_api_key then
function youtube:init(config)
if not config.google_api_key then
print('Missing config value: google_api_key.')
print('youtube.lua will not be enabled.')
return
@ -24,7 +24,7 @@ Returns the top result from YouTube.
Alias: /yt
```]]
function youtube:action(msg)
function youtube:action(msg, config)
local input = utilities.input(msg.text)
if not input then
@ -36,17 +36,17 @@ function youtube:action(msg)
end
end
local url = 'https://www.googleapis.com/youtube/v3/search?key=' .. self.config.google_api_key .. '&type=video&part=snippet&maxResults=4&q=' .. URL.escape(input)
local url = 'https://www.googleapis.com/youtube/v3/search?key=' .. config.google_api_key .. '&type=video&part=snippet&maxResults=4&q=' .. URL.escape(input)
local jstr, res = HTTPS.request(url)
if res ~= 200 then
utilities.send_reply(self, msg, self.config.errors.connection)
utilities.send_reply(self, msg, config.errors.connection)
return
end
local jdat = JSON.decode(jstr)
if jdat.pageInfo.totalResults == 0 then
utilities.send_reply(self, msg, self.config.errors.results)
utilities.send_reply(self, msg, config.errors.results)
return
end

View File

@ -137,18 +137,18 @@ function utilities.save_data(filename, data)
end
-- Gets coordinates for a location. Used by gMaps.lua, time.lua, weather.lua.
function utilities:get_coords(input)
function utilities:get_coords(input, config)
local url = 'http://maps.googleapis.com/maps/api/geocode/json?address=' .. URL.escape(input)
local jstr, res = HTTP.request(url)
if res ~= 200 then
return self.config.errors.connection
return config.errors.connection
end
local jdat = JSON.decode(jstr)
if jdat.status == 'ZERO_RESULTS' then
return self.config.errors.results
return config.errors.results
end
return {
@ -231,15 +231,15 @@ function utilities:user_from_message(msg, no_extra)
end
function utilities:handle_exception(err, message)
function utilities:handle_exception(err, message, config)
if not err then err = '' end
local output = '\n[' .. os.date('%F %T', os.time()) .. ']\n' .. self.info.username .. ': ' .. err .. '\n' .. message .. '\n'
if self.config.log_chat then
if config.log_chat then
output = '```' .. output .. '```'
utilities.send_message(self, self.config.log_chat, output, true, nil, true)
utilities.send_message(self, config.log_chat, output, true, nil, true)
else
print(output)
end