otouto 3.13
good lord
This commit is contained in:
@ -1,36 +1,19 @@
|
||||
local about = {}
|
||||
|
||||
local bot = require('otouto.bot')
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
local about = {}
|
||||
|
||||
about.command = 'about'
|
||||
about.doc = 'Returns information about the bot.'
|
||||
|
||||
about.triggers = {
|
||||
''
|
||||
}
|
||||
function about:init(config)
|
||||
about.text = config.about_text .. '\nBased on [otouto](http://github.com/topkecleon/otouto) v'..bot.version..' by topkecleon.'
|
||||
about.triggers = utilities.triggers(self.info.username, config.cmd_pat)
|
||||
:t('about'):t('start').table
|
||||
end
|
||||
|
||||
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 = config.about_text .. '\nBased on [otouto](http://github.com/topkecleon/otouto) v'..bot.version..' by topkecleon.'
|
||||
|
||||
if
|
||||
(msg.new_chat_member and msg.new_chat_member.id == self.info.id)
|
||||
or msg.text_lower:match('^'..config.cmd_pat..'about$')
|
||||
or msg.text_lower:match('^'..config.cmd_pat..'about@'..self.info.username:lower()..'$')
|
||||
or msg.text_lower:match('^'..config.cmd_pat..'start$')
|
||||
or msg.text_lower:match('^'..config.cmd_pat..'start@'..self.info.username:lower()..'$')
|
||||
then
|
||||
utilities.send_message(self, msg.chat.id, output, true, nil, true)
|
||||
return
|
||||
end
|
||||
|
||||
return true
|
||||
|
||||
utilities.send_message(self, msg.chat.id, about.text, true, nil, true)
|
||||
end
|
||||
|
||||
return about
|
||||
|
@ -9,8 +9,6 @@
|
||||
It requires tg (http://github.com/vysheng/tg) with supergroup support.
|
||||
For more documentation, read the the manual (otou.to/rtfm).
|
||||
|
||||
Remember to load this before blacklist.lua.
|
||||
|
||||
Important notices about updates will be here!
|
||||
|
||||
1.11 - Removed /kickme and /broadcast. Users should leave manually, and
|
||||
@ -20,6 +18,8 @@
|
||||
necessary.
|
||||
|
||||
1.11.1 - Bugfixes. /hammer can now be used in PM.
|
||||
|
||||
1.13 - Global banlist reinstated. Added default antiflood values to config. Bugfixes: Modding a user will no longer add him. Fixed kicks/bans in reply to join/leave notifications.
|
||||
]]
|
||||
|
||||
local JSON = require('dkjson')
|
||||
@ -36,11 +36,12 @@ function administration:init(config)
|
||||
admins = {},
|
||||
groups = {},
|
||||
activity = {},
|
||||
autokick_timer = os.date('%d')
|
||||
autokick_timer = os.date('%d'),
|
||||
globalbans = {}
|
||||
}
|
||||
end
|
||||
|
||||
self.admin_temp = {
|
||||
administration.temp = {
|
||||
help = {},
|
||||
flood = {}
|
||||
}
|
||||
@ -49,13 +50,25 @@ function administration:init(config)
|
||||
|
||||
administration.flags = administration.init_flags(config.cmd_pat)
|
||||
administration.init_command(self, config)
|
||||
administration.antiflood = config.administration.antiflood or {
|
||||
text = 5,
|
||||
voice = 5,
|
||||
audio = 5,
|
||||
contact = 5,
|
||||
photo = 10,
|
||||
video = 10,
|
||||
location = 10,
|
||||
document = 10,
|
||||
sticker = 20
|
||||
}
|
||||
|
||||
administration.doc = 'Returns a list of administrated groups.\nUse '..config.cmd_pat..'ahelp for more administrative commands.'
|
||||
administration.command = 'groups [query]'
|
||||
|
||||
-- In the worst case, don't send errors in reply to random messages.
|
||||
administration.error = false
|
||||
|
||||
-- Accept forwarded messages and messages from blacklisted users.
|
||||
administration.panoptic = true
|
||||
end
|
||||
|
||||
function administration.init_flags(cmd_pat) return {
|
||||
@ -106,18 +119,6 @@ function administration.init_flags(cmd_pat) return {
|
||||
}
|
||||
} end
|
||||
|
||||
administration.antiflood = {
|
||||
text = 5,
|
||||
voice = 5,
|
||||
audio = 5,
|
||||
contact = 5,
|
||||
photo = 10,
|
||||
video = 10,
|
||||
location = 10,
|
||||
document = 10,
|
||||
sticker = 20
|
||||
}
|
||||
|
||||
administration.ranks = {
|
||||
[0] = 'Banned',
|
||||
[1] = 'Users',
|
||||
@ -159,8 +160,8 @@ function administration:get_rank(user_id_str, chat_id_str, config)
|
||||
end
|
||||
end
|
||||
|
||||
-- Return 0 if the user_id_str is blacklisted (and antihammer is not enabled).
|
||||
if self.database.blacklist[user_id_str] then
|
||||
-- Return 0 if the user_id_str is globally banned (and antihammer is not enabled).
|
||||
if self.database.administration.globalbans[user_id_str] then
|
||||
return 0
|
||||
end
|
||||
|
||||
@ -172,8 +173,9 @@ end
|
||||
-- Returns an array of "user" tables.
|
||||
function administration:get_targets(msg, config)
|
||||
if msg.reply_to_message then
|
||||
local d = msg.reply_to_message.new_chat_member or msg.reply_to_message.left_chat_member or msg.reply_to_message.from
|
||||
local target = {}
|
||||
for k,v in pairs(msg.reply_to_message.from) do
|
||||
for k,v in pairs(d) do
|
||||
target[k] = v
|
||||
end
|
||||
target.name = utilities.build_name(target.first_name, target.last_name)
|
||||
@ -184,7 +186,7 @@ function administration:get_targets(msg, config)
|
||||
local input = utilities.input(msg.text)
|
||||
if input then
|
||||
local t = {}
|
||||
for _, user in ipairs(utilities.index(input)) do
|
||||
for user in input:gmatch('%g+') do
|
||||
if self.database.users[user] then
|
||||
local target = {}
|
||||
for k,v in pairs(self.database.users[user]) do
|
||||
@ -195,10 +197,11 @@ function administration:get_targets(msg, config)
|
||||
target.rank = administration.get_rank(self, target.id, msg.chat.id, config)
|
||||
table.insert(t, target)
|
||||
elseif tonumber(user) then
|
||||
local id = math.abs(tonumber(user))
|
||||
local target = {
|
||||
id = tonumber(user),
|
||||
id_str = user,
|
||||
name = 'Unknown ('..user..')',
|
||||
id = id,
|
||||
id_str = tostring(id),
|
||||
name = 'Unknown ('..id..')',
|
||||
rank = administration.get_rank(self, user, msg.chat.id, config)
|
||||
}
|
||||
table.insert(t, target)
|
||||
@ -227,7 +230,7 @@ function administration:mod_format(id)
|
||||
id = tostring(id)
|
||||
local user = self.database.users[id] or { first_name = 'Unknown' }
|
||||
local name = utilities.build_name(user.first_name, user.last_name)
|
||||
name = utilities.markdown_escape(name)
|
||||
name = utilities.md_escape(name)
|
||||
local output = '• ' .. name .. ' `[' .. id .. ']`\n'
|
||||
return output
|
||||
end
|
||||
@ -356,36 +359,36 @@ function administration.init_command(self_, config_)
|
||||
if not group.antiflood then
|
||||
group.antiflood = JSON.decode(JSON.encode(administration.antiflood))
|
||||
end
|
||||
if not self.admin_temp.flood[chat_id_str] then
|
||||
self.admin_temp.flood[chat_id_str] = {}
|
||||
if not administration.temp.flood[chat_id_str] then
|
||||
administration.temp.flood[chat_id_str] = {}
|
||||
end
|
||||
if not self.admin_temp.flood[chat_id_str][from_id_str] then
|
||||
self.admin_temp.flood[chat_id_str][from_id_str] = 0
|
||||
if not administration.temp.flood[chat_id_str][from_id_str] then
|
||||
administration.temp.flood[chat_id_str][from_id_str] = 0
|
||||
end
|
||||
if msg.sticker then -- Thanks Brazil for discarding switches.
|
||||
self.admin_temp.flood[chat_id_str][from_id_str] = self.admin_temp.flood[chat_id_str][from_id_str] + group.antiflood.sticker
|
||||
administration.temp.flood[chat_id_str][from_id_str] = administration.temp.flood[chat_id_str][from_id_str] + group.antiflood.sticker
|
||||
elseif msg.photo then
|
||||
self.admin_temp.flood[chat_id_str][from_id_str] = self.admin_temp.flood[chat_id_str][from_id_str] + group.antiflood.photo
|
||||
administration.temp.flood[chat_id_str][from_id_str] = administration.temp.flood[chat_id_str][from_id_str] + group.antiflood.photo
|
||||
elseif msg.document then
|
||||
self.admin_temp.flood[chat_id_str][from_id_str] = self.admin_temp.flood[chat_id_str][from_id_str] + group.antiflood.document
|
||||
administration.temp.flood[chat_id_str][from_id_str] = administration.temp.flood[chat_id_str][from_id_str] + group.antiflood.document
|
||||
elseif msg.audio then
|
||||
self.admin_temp.flood[chat_id_str][from_id_str] = self.admin_temp.flood[chat_id_str][from_id_str] + group.antiflood.audio
|
||||
administration.temp.flood[chat_id_str][from_id_str] = administration.temp.flood[chat_id_str][from_id_str] + group.antiflood.audio
|
||||
elseif msg.contact then
|
||||
self.admin_temp.flood[chat_id_str][from_id_str] = self.admin_temp.flood[chat_id_str][from_id_str] + group.antiflood.contact
|
||||
administration.temp.flood[chat_id_str][from_id_str] = administration.temp.flood[chat_id_str][from_id_str] + group.antiflood.contact
|
||||
elseif msg.video then
|
||||
self.admin_temp.flood[chat_id_str][from_id_str] = self.admin_temp.flood[chat_id_str][from_id_str] + group.antiflood.video
|
||||
administration.temp.flood[chat_id_str][from_id_str] = administration.temp.flood[chat_id_str][from_id_str] + group.antiflood.video
|
||||
elseif msg.location then
|
||||
self.admin_temp.flood[chat_id_str][from_id_str] = self.admin_temp.flood[chat_id_str][from_id_str] + group.antiflood.location
|
||||
administration.temp.flood[chat_id_str][from_id_str] = administration.temp.flood[chat_id_str][from_id_str] + group.antiflood.location
|
||||
elseif msg.voice then
|
||||
self.admin_temp.flood[chat_id_str][from_id_str] = self.admin_temp.flood[chat_id_str][from_id_str] + group.antiflood.voice
|
||||
administration.temp.flood[chat_id_str][from_id_str] = administration.temp.flood[chat_id_str][from_id_str] + group.antiflood.voice
|
||||
else
|
||||
self.admin_temp.flood[chat_id_str][from_id_str] = self.admin_temp.flood[chat_id_str][from_id_str] + group.antiflood.text
|
||||
administration.temp.flood[chat_id_str][from_id_str] = administration.temp.flood[chat_id_str][from_id_str] + group.antiflood.text
|
||||
end
|
||||
if self.admin_temp.flood[chat_id_str][from_id_str] > 99 then
|
||||
if administration.temp.flood[chat_id_str][from_id_str] > 99 then
|
||||
user.do_kick = true
|
||||
user.reason = 'antiflood'
|
||||
user.output = administration.flags[5].kicked:gsub('GROUPNAME', msg.chat.title)
|
||||
self.admin_temp.flood[chat_id_str][from_id_str] = nil
|
||||
administration.temp.flood[chat_id_str][from_id_str] = nil
|
||||
end
|
||||
end
|
||||
|
||||
@ -586,7 +589,7 @@ function administration.init_command(self_, config_)
|
||||
else
|
||||
local output = '*Commands for ' .. administration.ranks[rank] .. ':*\n'
|
||||
for i = 1, rank do
|
||||
for _, val in ipairs(self.admin_temp.help[i]) do
|
||||
for _, val in ipairs(administration.temp.help[i]) do
|
||||
output = output .. '• ' .. config.cmd_pat .. val .. '\n'
|
||||
end
|
||||
end
|
||||
@ -685,7 +688,7 @@ function administration.init_command(self_, config_)
|
||||
},
|
||||
|
||||
{ -- /motd
|
||||
triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('motd').table,
|
||||
triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('motd'):t('qotd').table,
|
||||
|
||||
command = 'motd',
|
||||
privilege = 1,
|
||||
@ -821,7 +824,7 @@ function administration.init_command(self_, config_)
|
||||
triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('setmotd', true):t('setqotd', true).table,
|
||||
|
||||
command = 'setmotd <motd>',
|
||||
privilege = 2,
|
||||
privilege = config_.administration.moderator_setmotd and 2 or 3,
|
||||
interior = true,
|
||||
doc = 'Sets the group\'s message of the day. Markdown is supported. Pass "--" to delete the message.',
|
||||
|
||||
@ -977,8 +980,7 @@ function administration.init_command(self_, config_)
|
||||
local output = ''
|
||||
local input = utilities.input(msg.text)
|
||||
if input then
|
||||
local index = utilities.index(input)
|
||||
for _, i in ipairs(index) do
|
||||
for i in input:gmatch('%g+') do
|
||||
local n = tonumber(i)
|
||||
if n and administration.flags[n] then
|
||||
if group.flags[n] == true then
|
||||
@ -1069,7 +1071,10 @@ function administration.init_command(self_, config_)
|
||||
group.bans[target.id_str] = nil
|
||||
end
|
||||
if group.grouptype == 'supergroup' then
|
||||
drua.channel_set_admin(msg.chat.id, target.id, 2)
|
||||
local chat_member = bindings.getChatMember(self, { chat_id = msg.chat.id, user_id = target.id })
|
||||
if chat_member and chat_member.result.status == 'member' then
|
||||
drua.channel_set_admin(msg.chat.id, target.id, 2)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1138,7 +1143,10 @@ function administration.init_command(self_, config_)
|
||||
utilities.send_reply(self, msg, target.name .. ' is the new governor.')
|
||||
end
|
||||
if group.grouptype == 'supergroup' then
|
||||
drua.channel_set_admin(msg.chat.id, target.id, 2)
|
||||
local chat_member = bindings.getChatMember(self, { chat_id = msg.chat.id, user_id = target.id })
|
||||
if chat_member and chat_member.result.status == 'member' then
|
||||
drua.channel_set_admin(msg.chat.id, target.id, 2)
|
||||
end
|
||||
administration.update_desc(self, msg.chat.id, config)
|
||||
end
|
||||
end
|
||||
@ -1195,7 +1203,7 @@ function administration.init_command(self_, config_)
|
||||
for _, target in ipairs(targets) do
|
||||
if target.err then
|
||||
output = output .. target.err .. '\n'
|
||||
elseif self.database.blacklist[target.id_str] then
|
||||
elseif self.database.administration.globalbans[target.id_str] then
|
||||
output = output .. target.name .. ' is already globally banned.\n'
|
||||
elseif target.rank >= administration.get_rank(self, msg.from.id, msg.chat.id, config) then
|
||||
output = output .. target.name .. ' is too privileged to be globally banned.\n'
|
||||
@ -1211,7 +1219,7 @@ function administration.init_command(self_, config_)
|
||||
end
|
||||
end
|
||||
end
|
||||
self.database.blacklist[target.id_str] = true
|
||||
self.database.administration.globalbans[target.id_str] = true
|
||||
if group and group.flags[6] == true then
|
||||
group.mods[target.id_str] = nil
|
||||
group.bans[target.id_str] = true
|
||||
@ -1243,10 +1251,10 @@ function administration.init_command(self_, config_)
|
||||
for _, target in ipairs(targets) do
|
||||
if target.err then
|
||||
output = output .. target.err .. '\n'
|
||||
elseif not self.database.blacklist[target.id_str] then
|
||||
elseif not self.database.administration.globalbans[target.id_str] then
|
||||
output = output .. target.name .. ' is not globally banned.\n'
|
||||
else
|
||||
self.database.blacklist[target.id_str] = nil
|
||||
self.database.administration.globalbans[target.id_str] = nil
|
||||
output = output .. target.name .. ' has been globally unbanned.\n'
|
||||
end
|
||||
end
|
||||
@ -1333,7 +1341,7 @@ function administration.init_command(self_, config_)
|
||||
|
||||
action = function(self, msg, group, config)
|
||||
if msg.chat.id == msg.from.id then
|
||||
utilities.send_message(self, msg.chat.id, 'No.')
|
||||
utilities.send_message(self, msg.chat.id, 'This is not a group.')
|
||||
elseif group then
|
||||
utilities.send_reply(self, msg, 'I am already administrating this group.')
|
||||
else
|
||||
@ -1344,8 +1352,7 @@ function administration.init_command(self_, config_)
|
||||
end
|
||||
local input = utilities.input(msg.text)
|
||||
if input then
|
||||
local index = utilities.index(input)
|
||||
for _, i in ipairs(index) do
|
||||
for i in input:gmatch('%g+') do
|
||||
local n = tonumber(i)
|
||||
if n and administration.flags[n] and flags[n] ~= true then
|
||||
flags[n] = true
|
||||
@ -1442,11 +1449,11 @@ function administration.init_command(self_, config_)
|
||||
-- Generate help messages and ahelp keywords.
|
||||
self_.database.administration.help = {}
|
||||
for i,_ in ipairs(administration.ranks) do
|
||||
self_.admin_temp.help[i] = {}
|
||||
administration.temp.help[i] = {}
|
||||
end
|
||||
for _,v in ipairs(administration.commands) do
|
||||
if v.command then
|
||||
table.insert(self_.admin_temp.help[v.privilege], v.command)
|
||||
table.insert(administration.temp.help[v.privilege], v.command)
|
||||
if v.doc then
|
||||
v.keyword = utilities.get_word(v.command, 1)
|
||||
end
|
||||
@ -1475,7 +1482,7 @@ function administration:action(msg, config)
|
||||
end
|
||||
|
||||
function administration:cron()
|
||||
self.admin_temp.flood = {}
|
||||
administration.temp.flood = {}
|
||||
if os.date('%d') ~= self.database.administration.autokick_timer then
|
||||
self.database.administration.autokick_timer = os.date('%d')
|
||||
for _,v in pairs(self.database.administration.groups) do
|
||||
|
@ -10,79 +10,47 @@ local utilities = require('otouto.utilities')
|
||||
apod.command = 'apod [date]'
|
||||
|
||||
function apod:init(config)
|
||||
apod.triggers = utilities.triggers(self.info.username, config.cmd_pat)
|
||||
:t('apod', true):t('apodhd', true):t('apodtext', true).table
|
||||
apod.doc = config.cmd_pat .. [[apod [query]
|
||||
apod.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('apod', true).table
|
||||
apod.doc = [[
|
||||
/apod [YYYY-MM-DD]
|
||||
Returns the Astronomy Picture of the Day.
|
||||
If the query is a date, in the format YYYY-MM-DD, the APOD of that day is returned.
|
||||
Examples:
|
||||
]] .. config.cmd_pat .. [[apodhd [query]
|
||||
Returns the image in HD, if available.
|
||||
|
||||
]] .. config.cmd_pat .. [[apodtext [query]
|
||||
Returns the explanation of the APOD.
|
||||
|
||||
Source: nasa.gov]]
|
||||
Source: nasa.gov
|
||||
]]
|
||||
apod.doc = apod.doc:gsub('/', config.cmd_pat)
|
||||
apod.base_url = 'https://api.nasa.gov/planetary/apod?api_key=' .. (config.nasa_api_key or 'DEMO_KEY')
|
||||
end
|
||||
|
||||
function apod:action(msg, config)
|
||||
|
||||
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=' .. config.nasa_api_key
|
||||
|
||||
local url = apod.base_url
|
||||
local date = os.date('%F')
|
||||
if input then
|
||||
if input:match('(%d+)%-(%d+)%-(%d+)$') then
|
||||
if input:match('^(%d+)%-(%d+)%-(%d+)$') then
|
||||
url = url .. '&date=' .. URL.escape(input)
|
||||
date = date .. input
|
||||
else
|
||||
utilities.send_message(self, msg.chat.id, apod.doc, true, msg.message_id, true)
|
||||
return
|
||||
date = input
|
||||
end
|
||||
else
|
||||
date = date .. os.date("%F")
|
||||
end
|
||||
|
||||
date = date .. '*\n'
|
||||
|
||||
local jstr, res = HTTPS.request(url)
|
||||
if res ~= 200 then
|
||||
local jstr, code = HTTPS.request(url)
|
||||
if code ~= 200 then
|
||||
utilities.send_reply(self, msg, config.errors.connection)
|
||||
return
|
||||
end
|
||||
|
||||
local jdat = JSON.decode(jstr)
|
||||
|
||||
if jdat.error then
|
||||
local data = JSON.decode(jstr)
|
||||
if data.error then
|
||||
utilities.send_reply(self, msg, config.errors.results)
|
||||
return
|
||||
end
|
||||
|
||||
local img_url = jdat.url
|
||||
|
||||
if string.match(msg.text, '^'..config.cmd_pat..'apodhd*') then
|
||||
img_url = jdat.hdurl or jdat.url
|
||||
end
|
||||
|
||||
local output = date .. '[' .. jdat.title .. '](' .. img_url .. ')'
|
||||
|
||||
if string.match(msg.text, '^'..config.cmd_pat..'apodtext*') then
|
||||
output = output .. '\n' .. jdat.explanation
|
||||
disable_page_preview = true
|
||||
end
|
||||
|
||||
if jdat.copyright then
|
||||
output = output .. '\nCopyright: ' .. jdat.copyright
|
||||
end
|
||||
|
||||
utilities.send_message(self, msg.chat.id, output, disable_page_preview, nil, true)
|
||||
|
||||
local output = string.format(
|
||||
'<b>%s (</b><a href="%s">%s</a><b>)</b>\n%s',
|
||||
utilities.html_escape(data.title),
|
||||
utilities.html_escape(data.hdurl or data.url),
|
||||
date,
|
||||
utilities.html_escape(data.explanation)
|
||||
)
|
||||
utilities.send_message(self, msg.chat.id, output, false, nil, 'html')
|
||||
end
|
||||
|
||||
return apod
|
||||
|
@ -5,11 +5,9 @@ local URL = require('socket.url')
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
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
|
||||
end
|
||||
assert(config.biblia_api_key,
|
||||
'bible.lua requires a Biblia API key from http://api.biblia.com.'
|
||||
)
|
||||
|
||||
bible.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('bible', true):t('b', true).table
|
||||
bible.doc = config.cmd_pat .. [[bible <reference>
|
||||
@ -21,9 +19,9 @@ bible.command = 'bible <reference>'
|
||||
|
||||
function bible:action(msg, config)
|
||||
|
||||
local input = utilities.input(msg.text)
|
||||
local input = utilities.input_from_msg(msg)
|
||||
if not input then
|
||||
utilities.send_message(self, msg.chat.id, bible.doc, true, msg.message_id, true)
|
||||
utilities.send_reply(self, msg, bible.doc, true)
|
||||
return
|
||||
end
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
-- Credit to Juan (tg:JuanPotato; gh:JuanPotato) for this plugin.
|
||||
-- Or rather, the seven lines that actually mean anything.
|
||||
|
||||
local bing = {}
|
||||
|
||||
@ -15,54 +14,65 @@ bing.command = 'bing <query>'
|
||||
bing.search_url = 'https://api.datamarket.azure.com/Data.ashx/Bing/Search/Web?Query=\'%s\'&$format=json'
|
||||
|
||||
function bing:init(config)
|
||||
if not config.bing_api_key then
|
||||
print('Missing config value: bing_api_key.')
|
||||
print('bing.lua will not be enabled.')
|
||||
return
|
||||
end
|
||||
bing.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('bing', true):t('g', true):t('google', true).table
|
||||
bing.doc = config.cmd_pat .. [[bing <query>
|
||||
Returns the top web search results from Bing.
|
||||
Aliases: ]] .. config.cmd_pat .. 'g, ' .. config.cmd_pat .. 'google'
|
||||
assert(config.bing_api_key,
|
||||
'bing.lua requires a Bing API key from http://datamarket.azure.com/dataset/bing/search.'
|
||||
)
|
||||
|
||||
bing.headers = { ["Authorization"] = "Basic " .. mime.b64(":" .. config.bing_api_key) }
|
||||
bing.triggers = utilities.triggers(self.info.username, config.cmd_pat)
|
||||
:t('bing', true):t('g', true):t('google', true).table
|
||||
bing.doc = [[
|
||||
/bing <query>
|
||||
Returns the top web results from Bing.
|
||||
Aliases: /g, /google
|
||||
]]
|
||||
bing.doc = bing.doc:gsub('/', config.cmd_pat)
|
||||
|
||||
end
|
||||
|
||||
function bing:action(msg, config)
|
||||
local input = utilities.input(msg.text)
|
||||
local input = utilities.input_from_msg(msg)
|
||||
if not input then
|
||||
if msg.reply_to_message and msg.reply_to_message.text ~= '' then
|
||||
input = msg.reply_to_message.text
|
||||
else
|
||||
utilities.send_reply(self, msg, bing.doc, true)
|
||||
return
|
||||
end
|
||||
utilities.send_reply(self, msg, bing.doc, true)
|
||||
return
|
||||
end
|
||||
|
||||
local url = bing.search_url:format(URL.escape(input))
|
||||
local resbody = {}
|
||||
local _,b,_ = https.request{
|
||||
local _, code = https.request{
|
||||
url = url,
|
||||
headers = { ["Authorization"] = "Basic " .. mime.b64(":" .. config.bing_api_key) },
|
||||
headers = bing.headers,
|
||||
sink = ltn12.sink.table(resbody),
|
||||
}
|
||||
if b ~= 200 then
|
||||
if code ~= 200 then
|
||||
utilities.send_reply(self, msg, config.errors.connection)
|
||||
return
|
||||
end
|
||||
|
||||
local data = JSON.decode(table.concat(resbody))
|
||||
-- Four results in a group, eight in private.
|
||||
local limit = msg.chat.type == 'private' and 8 or 4
|
||||
-- No more results than provided.
|
||||
limit = limit > #data.d.results and #data.d.results or limit
|
||||
if limit == 0 then
|
||||
utilities.send_reply(self, msg, config.errors.results)
|
||||
return
|
||||
end
|
||||
local dat = JSON.decode(table.concat(resbody))
|
||||
local limit = 4
|
||||
if msg.chat.type == 'private' then
|
||||
limit = 8
|
||||
end
|
||||
if limit > #dat.d.results then
|
||||
limit = #dat.d.results
|
||||
end
|
||||
|
||||
local reslist = {}
|
||||
for i = 1, limit do
|
||||
local result = dat.d.results[i]
|
||||
local s = '• [' .. result.Title:gsub('%]', '\\]') .. '](' .. result.Url:gsub('%)', '\\)') .. ')'
|
||||
table.insert(reslist, s)
|
||||
table.insert(reslist, string.format(
|
||||
'• <a href="%s">%s</a>',
|
||||
utilities.html_escape(data.d.results[i].Url),
|
||||
utilities.html_escape(data.d.results[i].Title)
|
||||
))
|
||||
end
|
||||
local output = '*Search results for* _' .. utilities.md_escape(input) .. '_ *:*\n' .. table.concat(reslist, '\n')
|
||||
utilities.send_message(self, msg.chat.id, output, true, nil, true)
|
||||
local output = string.format(
|
||||
'<b>Search results for</b> <i>%s</i><b>:</b>\n%s',
|
||||
utilities.html_escape(input),
|
||||
table.concat(reslist, '\n')
|
||||
)
|
||||
utilities.send_message(self, msg.chat.id, output, true, nil, 'html')
|
||||
end
|
||||
|
||||
return bing
|
||||
|
@ -1,39 +1,15 @@
|
||||
-- This plugin will allow the admin to blacklist users who will be unable to
|
||||
-- use the bot. This plugin should be at the top of your plugin list in config.
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
local blacklist = {}
|
||||
|
||||
local utilities = require('otouto.utilities')
|
||||
local bindings = require('otouto.bindings')
|
||||
|
||||
function blacklist:init()
|
||||
if not self.database.blacklist then
|
||||
self.database.blacklist = {}
|
||||
end
|
||||
function blacklist:init(config)
|
||||
blacklist.triggers = utilities.triggers(self.info.username, config.cmd_pat)
|
||||
:t('blacklist', true):t('unblacklist', true).table
|
||||
blacklist.error = false
|
||||
end
|
||||
|
||||
blacklist.triggers = {
|
||||
''
|
||||
}
|
||||
|
||||
blacklist.error = false
|
||||
|
||||
function blacklist:action(msg, config)
|
||||
if self.database.blacklist[tostring(msg.from.id)] then
|
||||
return
|
||||
elseif self.database.blacklist[tostring(msg.chat.id)] then
|
||||
bindings.leaveChat(self, { chat_id = msg.chat.id })
|
||||
return
|
||||
end
|
||||
if not (
|
||||
msg.from.id == config.admin
|
||||
and (
|
||||
msg.text:match('^'..config.cmd_pat..'blacklist')
|
||||
or msg.text:match('^'..config.cmd_pat..'unblacklist')
|
||||
)
|
||||
) then
|
||||
return true
|
||||
end
|
||||
if msg.from.id ~= config.admin then return true end
|
||||
local targets = {}
|
||||
if msg.reply_to_message then
|
||||
table.insert(targets, {
|
||||
@ -44,7 +20,7 @@ function blacklist:action(msg, config)
|
||||
else
|
||||
local input = utilities.input(msg.text)
|
||||
if input then
|
||||
for _, user in ipairs(utilities.index(input)) do
|
||||
for user in input:gmatch('%g+') do
|
||||
if self.database.users[user] then
|
||||
table.insert(targets, {
|
||||
id = self.database.users[user].id,
|
||||
|
@ -13,29 +13,16 @@ Returns solutions to mathematical expressions and conversions between common uni
|
||||
end
|
||||
|
||||
function calc:action(msg, config)
|
||||
|
||||
local input = utilities.input(msg.text)
|
||||
local input = utilities.input_from_msg(msg)
|
||||
if not input then
|
||||
if msg.reply_to_message and msg.reply_to_message.text then
|
||||
input = msg.reply_to_message.text
|
||||
else
|
||||
utilities.send_message(self, msg.chat.id, calc.doc, true, msg.message_id, true)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local url = 'https://api.mathjs.org/v1/?expr=' .. URL.escape(input)
|
||||
|
||||
local output = HTTPS.request(url)
|
||||
if not output then
|
||||
utilities.send_reply(self, msg, config.errors.connection)
|
||||
utilities.send_reply(self, msg, calc.doc, true)
|
||||
return
|
||||
end
|
||||
|
||||
output = '`' .. output .. '`'
|
||||
|
||||
utilities.send_message(self, msg.chat.id, output, true, msg.message_id, true)
|
||||
|
||||
local url = 'https://api.mathjs.org/v1/?expr=' .. URL.escape(input)
|
||||
local output = HTTPS.request(url)
|
||||
output = output and '`'..output..'`' or config.errors.connection
|
||||
utilities.send_reply(self, msg, output, true)
|
||||
end
|
||||
|
||||
return calc
|
||||
|
28
otouto/plugins/catfact.lua
Normal file
28
otouto/plugins/catfact.lua
Normal file
@ -0,0 +1,28 @@
|
||||
-- Based on a plugin by matthewhesketh.
|
||||
|
||||
local JSON = require('dkjson')
|
||||
local HTTP = require('socket.http')
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
local catfact = {}
|
||||
|
||||
function catfact:init(config)
|
||||
catfact.triggers = utilities.triggers(self.info.username, config.cmd_pat)
|
||||
:t('catfact', true).table
|
||||
catfact.command = 'catfact'
|
||||
catfact.doc = 'Returns a cat fact.'
|
||||
catfact.url = 'http://catfacts-api.appspot.com/api/facts'
|
||||
end
|
||||
|
||||
function catfact:action(msg, config)
|
||||
local jstr, code = HTTP.request(catfact.url)
|
||||
if code ~= 200 then
|
||||
utilities.send_reply(self, msg, config.errors.connection)
|
||||
return
|
||||
end
|
||||
local data = JSON.decode(jstr)
|
||||
local output = '*Cat Fact*\n_' .. data.facts[1] .. '_'
|
||||
utilities.send_message(self, msg.chat.id, output, true, nil, true)
|
||||
end
|
||||
|
||||
return catfact
|
@ -1,80 +0,0 @@
|
||||
-- Put this absolutely at the end, even after greetings.lua.
|
||||
|
||||
local chatter = {}
|
||||
|
||||
local HTTP = require('socket.http')
|
||||
local URL = require('socket.url')
|
||||
local JSON = require('dkjson')
|
||||
local bindings = require('otouto.bindings')
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
function chatter:init(config)
|
||||
if not config.simsimi_key then
|
||||
print('Missing config value: simsimi_key.')
|
||||
print('chatter.lua will not be enabled.')
|
||||
return
|
||||
end
|
||||
|
||||
chatter.triggers = {
|
||||
''
|
||||
}
|
||||
end
|
||||
|
||||
chatter.base_url = 'http://%sapi.simsimi.com/request.p?key=%s&lc=%s&ft=1.0&text=%s'
|
||||
|
||||
function chatter:action(msg, config)
|
||||
|
||||
if msg.text == '' then return true end
|
||||
|
||||
if (
|
||||
not (
|
||||
msg.text_lower:match('^'..self.info.first_name:lower()..',')
|
||||
or msg.text_lower:match('^@'..self.info.username:lower()..',')
|
||||
or msg.from.id == msg.chat.id
|
||||
--Uncomment the following line for Al Gore-like conversation.
|
||||
--or (msg.reply_to_message and msg.reply_to_message.from.id == self.info.id)
|
||||
)
|
||||
or msg.text:match('^'..config.cmd_pat)
|
||||
or msg.text == ''
|
||||
) then
|
||||
return true
|
||||
end
|
||||
|
||||
bindings.sendChatAction(self, { action = 'typing' } )
|
||||
|
||||
local input = msg.text_lower:gsub(self.info.first_name, 'simsimi')
|
||||
input = input:gsub('@'..self.info.username, 'simsimi')
|
||||
|
||||
local sandbox = config.simsimi_trial and 'sandbox.' or ''
|
||||
|
||||
local url = chatter.base_url:format(sandbox, config.simsimi_key, config.lang, URL.escape(input))
|
||||
|
||||
local jstr, res = HTTP.request(url)
|
||||
if res ~= 200 then
|
||||
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, config.errors.chatter_response)
|
||||
return
|
||||
end
|
||||
local output = jdat.response
|
||||
|
||||
-- Clean up the response here.
|
||||
output = utilities.trim(output)
|
||||
-- Simsimi will often refer to itself. Replace "simsimi" with the bot name.
|
||||
output = output:gsub('%aimi?%aimi?', self.info.first_name)
|
||||
-- Self-explanatory.
|
||||
output = output:gsub('USER', msg.from.first_name)
|
||||
-- Capitalize the first letter.
|
||||
output = output:gsub('^%l', string.upper)
|
||||
-- Add a period if there is no punctuation.
|
||||
output = output:gsub('%P$', '%1.')
|
||||
|
||||
utilities.send_message(self, msg.chat.id, output)
|
||||
|
||||
end
|
||||
|
||||
return chatter
|
28
otouto/plugins/chuckfact.lua
Normal file
28
otouto/plugins/chuckfact.lua
Normal file
@ -0,0 +1,28 @@
|
||||
-- Based on a plugin by matthewhesketh.
|
||||
|
||||
local JSON = require('dkjson')
|
||||
local HTTP = require('socket.http')
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
local chuck = {}
|
||||
|
||||
function chuck:init(config)
|
||||
chuck.triggers = utilities.triggers(self.info.username, config.cmd_pat)
|
||||
:t('chuck', true):t('cn', true):t('chucknorris', true).table
|
||||
chuck.command = 'chuck'
|
||||
chuck.doc = 'Returns a fact about Chuck Norris.'
|
||||
chuck.url = 'http://api.icndb.com/jokes/random'
|
||||
end
|
||||
|
||||
function chuck:action(msg, config)
|
||||
local jstr, code = HTTP.request(chuck.url)
|
||||
if code ~= 200 then
|
||||
utilities.send_reply(self, msg, config.errors.connection)
|
||||
return
|
||||
end
|
||||
local data = JSON.decode(jstr)
|
||||
local output = '*Chuck Norris Fact*\n_' .. data.value.joke .. '_'
|
||||
utilities.send_message(self, msg.chat.id, output, true, nil, true)
|
||||
end
|
||||
|
||||
return chuck
|
36
otouto/plugins/cleverbot.lua
Normal file
36
otouto/plugins/cleverbot.lua
Normal file
@ -0,0 +1,36 @@
|
||||
local HTTPS = require('ssl.https')
|
||||
local URL = require('socket.url')
|
||||
local JSON = require('dkjson')
|
||||
local utilities = require('otouto.utilities')
|
||||
local bindings = require('otouto.bindings')
|
||||
|
||||
local cleverbot = {}
|
||||
|
||||
function cleverbot:init(config)
|
||||
cleverbot.name = '^' .. self.info.first_name:lower() .. ', '
|
||||
cleverbot.username = '^@' .. self.info.username:lower() .. ', '
|
||||
cleverbot.triggers = {
|
||||
'^' .. self.info.first_name:lower() .. ', ',
|
||||
'^@' .. self.info.username:lower() .. ', '
|
||||
}
|
||||
cleverbot.url = config.chatter.cleverbot_api
|
||||
cleverbot.error = false
|
||||
end
|
||||
|
||||
function cleverbot:action(msg, config)
|
||||
bindings.sendChatAction(self, { chat_id = msg.chat.id, action = 'typing' })
|
||||
local input = msg.text_lower:gsub(cleverbot.name, ''):gsub(cleverbot.name, '')
|
||||
local jstr, code = HTTPS.request(cleverbot.url .. URL.escape(input))
|
||||
if code ~= 200 then
|
||||
utilities.send_message(self, msg.chat.id, config.chatter.connection)
|
||||
return
|
||||
end
|
||||
local data = JSON.decode(jstr)
|
||||
if not data.clever then
|
||||
utilities.send_message(self, msg.chat.id, config.chatter.response)
|
||||
return
|
||||
end
|
||||
utilities.send_message(self, msg.chat.id, data.clever)
|
||||
end
|
||||
|
||||
return cleverbot
|
@ -1,8 +1,8 @@
|
||||
-- Commits from https://github.com/ngerakines/commitment.
|
||||
|
||||
local commit = {}
|
||||
|
||||
local utilities = require('otouto.utilities')
|
||||
local bindings = require('otouto.bindings')
|
||||
local http = require('socket.http')
|
||||
|
||||
commit.command = 'commit'
|
||||
commit.doc = 'Returns a commit message from whatthecommit.com.'
|
||||
@ -11,420 +11,16 @@ function commit:init(config)
|
||||
commit.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('commit').table
|
||||
end
|
||||
|
||||
local commits = {
|
||||
"One does not simply merge into master",
|
||||
"Merging the merge",
|
||||
"Another bug bites the dust",
|
||||
"de-misunderestimating",
|
||||
"Some shit.",
|
||||
"add actual words",
|
||||
"I CAN HAZ COMMENTZ.",
|
||||
"giggle.",
|
||||
"Whatever.",
|
||||
"Finished fondling.",
|
||||
"FONDLED THE CODE",
|
||||
"this is how we generate our shit.",
|
||||
"unh",
|
||||
"It works!",
|
||||
"unionfind is no longer being molested.",
|
||||
"Well, it's doing something.",
|
||||
"I'M PUSHING.",
|
||||
"Whee.",
|
||||
"Whee, good night.",
|
||||
"It'd be nice if type errors caused the compiler to issue a type error",
|
||||
"Fucking templates.",
|
||||
"I hate this fucking language.",
|
||||
"marks",
|
||||
"that coulda been bad",
|
||||
"hoo boy",
|
||||
"It was the best of times, it was the worst of times",
|
||||
"Fucking egotistical bastard. adds expandtab to vimrc",
|
||||
"if you're not using et, fuck off",
|
||||
"WHO THE FUCK CAME UP WITH MAKE?",
|
||||
"This is a basic implementation that works.",
|
||||
"By works, I meant 'doesnt work'. Works now..",
|
||||
"Last time I said it works? I was kidding. Try this.",
|
||||
"Just stop reading these for a while, ok..",
|
||||
"Give me a break, it's 2am. But it works now.",
|
||||
"Make that it works in 90% of the cases. 3:30.",
|
||||
"Ok, 5am, it works. For real.",
|
||||
"FOR REAL.",
|
||||
"I don't know what these changes are supposed to accomplish but somebody told me to make them.",
|
||||
"I don't get paid enough for this shit.",
|
||||
"fix some fucking errors",
|
||||
"first blush",
|
||||
"So my boss wanted this button ...",
|
||||
"uhhhhhh",
|
||||
"forgot we're not using a smart language",
|
||||
"include shit",
|
||||
"To those I leave behind, good luck!",
|
||||
"things occurred",
|
||||
"i dunno, maybe this works",
|
||||
"8==========D",
|
||||
"No changes made",
|
||||
"whooooooooooooooooooooooooooo",
|
||||
"clarify further the brokenness of C++. why the fuck are we using C++?",
|
||||
".",
|
||||
"Friday 5pm",
|
||||
"changes",
|
||||
"A fix I believe, not like I tested or anything",
|
||||
"Useful text",
|
||||
"pgsql is being a pain",
|
||||
"pgsql is more strict, increase the hackiness up to 11",
|
||||
"c&p fail",
|
||||
"syntax",
|
||||
"fix",
|
||||
"just shoot me",
|
||||
"arrrggghhhhh fixed!",
|
||||
"someone fails and it isn't me",
|
||||
"totally more readable",
|
||||
"better grepping",
|
||||
"fix",
|
||||
"fix bug, for realz",
|
||||
"fix /sigh",
|
||||
"Does this work",
|
||||
"MOAR BIFURCATION",
|
||||
"bifurcation",
|
||||
"REALLY FUCKING FIXED",
|
||||
"FIX",
|
||||
"better ignores",
|
||||
"More ignore",
|
||||
"more ignores",
|
||||
"more ignores",
|
||||
"more ignores",
|
||||
"more ignores",
|
||||
"more ignores",
|
||||
"more ignored words",
|
||||
"more fixes",
|
||||
"really ignore ignored worsd",
|
||||
"fixes",
|
||||
"/sigh",
|
||||
"fix",
|
||||
"fail",
|
||||
"pointless limitation",
|
||||
"omg what have I done?",
|
||||
"added super-widget 2.0.",
|
||||
"tagging release w.t.f.",
|
||||
"I can't believe it took so long to fix this.",
|
||||
"I must have been drunk.",
|
||||
"This is why the cat shouldn't sit on my keyboard.",
|
||||
"This is why git rebase is a horrible horrible thing.",
|
||||
"ajax-loader hotness, oh yeah",
|
||||
"small is a real HTML tag, who knew.",
|
||||
"WTF is this.",
|
||||
"Do things better, faster, stronger",
|
||||
"Use a real JS construct, WTF knows why this works in chromium.",
|
||||
"Added a banner to the default admin page. Please have mercy on me =(",
|
||||
"needs more cow bell",
|
||||
"Switched off unit test X because the build had to go out now and there was no time to fix it properly.",
|
||||
"Updated",
|
||||
"I must sleep... it's working... in just three hours...",
|
||||
"I was wrong...",
|
||||
"Completed with no bugs...",
|
||||
"Fixed a little bug...",
|
||||
"Fixed a bug in NoteLineCount... not seriously...",
|
||||
"woa!! this one was really HARD!",
|
||||
"Made it to compile...",
|
||||
"changed things...",
|
||||
"touched...",
|
||||
"i think i fixed a bug...",
|
||||
"perfect...",
|
||||
"Moved something to somewhere... goodnight...",
|
||||
"oops, forgot to add the file",
|
||||
"Corrected mistakes",
|
||||
"oops",
|
||||
"oops!",
|
||||
"put code that worked where the code that didn't used to be",
|
||||
"Nothing to see here, move along",
|
||||
"I am even stupider than I thought",
|
||||
"I don't know what the hell I was thinking.",
|
||||
"fixed errors in the previous commit",
|
||||
"Committed some changes",
|
||||
"Some bugs fixed",
|
||||
"Minor updates",
|
||||
"Added missing file in previous commit",
|
||||
"bug fix",
|
||||
"typo",
|
||||
"bara bra grejjor",
|
||||
"Continued development...",
|
||||
"Does anyone read this? I'll be at the coffee shop accross the street.",
|
||||
"That's just how I roll",
|
||||
"work in progress",
|
||||
"minor changes",
|
||||
"some brief changes",
|
||||
"assorted changes",
|
||||
"lots and lots of changes",
|
||||
"another big bag of changes",
|
||||
"lots of changes after a lot of time",
|
||||
"LOTS of changes. period",
|
||||
"Test commit. Please ignore",
|
||||
"I'm just a grunt. Don't blame me for this awful PoS.",
|
||||
"I did it for the lulz!",
|
||||
"I'll explain this when I'm sober .. or revert it",
|
||||
"Obligatory placeholder commit message",
|
||||
"A long time ago, in a galaxy far far away...",
|
||||
"Fixed the build.",
|
||||
"various changes",
|
||||
"One more time, but with feeling.",
|
||||
"Handled a particular error.",
|
||||
"Fixed unnecessary bug.",
|
||||
"Removed code.",
|
||||
"Added translation.",
|
||||
"Updated build targets.",
|
||||
"Refactored configuration.",
|
||||
"Locating the required gigapixels to render...",
|
||||
"Spinning up the hamster...",
|
||||
"Shovelling coal into the server...",
|
||||
"Programming the flux capacitor",
|
||||
"The last time I tried this the monkey didn't survive. Let's hope it works better this time.",
|
||||
"I should have had a V8 this morning.",
|
||||
"640K ought to be enough for anybody",
|
||||
"pay no attention to the man behind the curtain",
|
||||
"a few bits tried to escape, but we caught them",
|
||||
"Who has two thumbs and remembers the rudiments of his linear algebra courses? Apparently, this guy.",
|
||||
"workaround for ant being a pile of fail",
|
||||
"Don't push this commit",
|
||||
"rats",
|
||||
"squash me",
|
||||
"fixed mistaken bug",
|
||||
"Final commit, ready for tagging",
|
||||
"-m \'So I hear you like commits ...\'",
|
||||
"epic",
|
||||
"need another beer",
|
||||
"Well the book was obviously wrong.",
|
||||
"lolwhat?",
|
||||
"Another commit to keep my CAN streak going.",
|
||||
"I cannot believe that it took this long to write a test for this.",
|
||||
"TDD: 1, Me: 0",
|
||||
"Yes, I was being sarcastic.",
|
||||
"Apparently works-for-me is a crappy excuse.",
|
||||
"tl;dr",
|
||||
"I would rather be playing SC2.",
|
||||
"Crap. Tonight is raid night and I am already late.",
|
||||
"I know what I am doing. Trust me.",
|
||||
"You should have trusted me.",
|
||||
"Is there an award for this?",
|
||||
"Is there an achievement for this?",
|
||||
"I'm totally adding this to epic win. +300",
|
||||
"This really should not take 19 minutes to build.",
|
||||
"fixed the israeli-palestinian conflict",
|
||||
"SHIT ===> GOLD",
|
||||
"Committing in accordance with the prophecy.",
|
||||
"It compiles! Ship it!",
|
||||
"LOL!",
|
||||
"Reticulating splines...",
|
||||
"SEXY RUSSIAN CODES WAITING FOR YOU TO CALL",
|
||||
"s/import/include/",
|
||||
"extra debug for stuff module",
|
||||
"debug line test",
|
||||
"debugo",
|
||||
"remove debug<br/>all good",
|
||||
"debug suff",
|
||||
"more debug... who overwrote!",
|
||||
"these confounded tests drive me nuts",
|
||||
"For great justice.",
|
||||
"QuickFix.",
|
||||
"oops - thought I got that one.",
|
||||
"removed echo and die statements, lolz.",
|
||||
"somebody keeps erasing my changes.",
|
||||
"doh.",
|
||||
"pam anderson is going to love me.",
|
||||
"added security.",
|
||||
"arrgghh... damn this thing for not working.",
|
||||
"jobs... steve jobs",
|
||||
"and a comma",
|
||||
"this is my quickfix branch and i will use to do my quickfixes",
|
||||
"Fix my stupidness",
|
||||
"and so the crazy refactoring process sees the sunlight after some months in the dark!",
|
||||
"gave up and used tables.",
|
||||
"[Insert your commit message here. Be sure to make it descriptive.]",
|
||||
"Removed test case since code didn't pass QA",
|
||||
"removed tests since i can't make them green",
|
||||
"stuff",
|
||||
"more stuff",
|
||||
"Become a programmer, they said. It'll be fun, they said.",
|
||||
"Same as last commit with changes",
|
||||
"foo",
|
||||
"just checking if git is working properly...",
|
||||
"fixed some minor stuff, might need some additional work.",
|
||||
"just trolling the repo",
|
||||
"All your codebase are belong to us.",
|
||||
"Somebody set up us the bomb.",
|
||||
"should work I guess...",
|
||||
"To be honest, I do not quite remember everything I changed here today. But it is all good, I tell ya.",
|
||||
"well crap.",
|
||||
"herpderp (redux)",
|
||||
"herpderp",
|
||||
"Derp",
|
||||
"derpherp",
|
||||
"Herping the derp",
|
||||
"sometimes you just herp the derp so hard it herpderps",
|
||||
"Derp. Fix missing constant post rename",
|
||||
"Herping the fucking derp right here and now.",
|
||||
"Derp, asset redirection in dev mode",
|
||||
"mergederp",
|
||||
"Derp search/replace fuckup",
|
||||
"Herpy dooves.",
|
||||
"Derpy hooves",
|
||||
"derp, helper method rename",
|
||||
"Herping the derp derp (silly scoping error)",
|
||||
"Herp derp I left the debug in there and forgot to reset errors.",
|
||||
"Reset error count between rows. herpderp",
|
||||
"hey, what's that over there?!",
|
||||
"hey, look over there!",
|
||||
"It worked for me...",
|
||||
"Does not work.",
|
||||
"Either Hot Shit or Total Bollocks",
|
||||
"Arrrrgggg",
|
||||
"Don’t mess with Voodoo",
|
||||
"I expected something different.",
|
||||
"Todo!!!",
|
||||
"This is supposed to crash",
|
||||
"No changes after this point.",
|
||||
"I know, I know, this is not how I’m supposed to do it, but I can't think of something better.",
|
||||
"Don’t even try to refactor it.",
|
||||
"(c) Microsoft 1988",
|
||||
"Please no changes this time.",
|
||||
"Why The Fuck?",
|
||||
"We should delete this crap before shipping.",
|
||||
"Shit code!",
|
||||
"ALL SORTS OF THINGS",
|
||||
"Herpderp, shoulda check if it does really compile.",
|
||||
"I CAN HAZ PYTHON, I CAN HAZ INDENTS",
|
||||
"Major fixup.",
|
||||
"less french words",
|
||||
"breathe, =, breathe",
|
||||
"IEize",
|
||||
"this doesn't really make things faster, but I tried",
|
||||
"this should fix it",
|
||||
"forgot to save that file",
|
||||
"Glue. Match sticks. Paper. Build script!",
|
||||
"Argh! About to give up :(",
|
||||
"Blaming regex.",
|
||||
"oops",
|
||||
"it's friday",
|
||||
"yo recipes",
|
||||
"Not sure why",
|
||||
"lol digg",
|
||||
"grrrr",
|
||||
"For real, this time.",
|
||||
"Feed. You. Stuff. No time.",
|
||||
"I don't give a damn 'bout my reputation",
|
||||
"DEAL WITH IT",
|
||||
"commit",
|
||||
"tunning",
|
||||
"I really should've committed this when I finished it...",
|
||||
"It's getting hard to keep up with the crap I've trashed",
|
||||
"I honestly wish I could remember what was going on here...",
|
||||
"I must enjoy torturing myself",
|
||||
"For the sake of my sanity, just ignore this...",
|
||||
"That last commit message about silly mistakes pales in comparision to this one",
|
||||
"My bad",
|
||||
"Still can't get this right...",
|
||||
"Nitpicking about alphabetizing methods, minor OCD thing",
|
||||
"Committing fixes in the dark, seriously, who killed my power!?",
|
||||
"You can't see it, but I'm making a very angry face right now",
|
||||
"Fix the fixes",
|
||||
"It's secret!",
|
||||
"Commit committed....",
|
||||
"No time to commit.. My people need me!",
|
||||
"Something fixed",
|
||||
"I'm hungry",
|
||||
"asdfasdfasdfasdfasdfasdfadsf",
|
||||
"hmmm",
|
||||
"formatted all",
|
||||
"Replace all whitespaces with tabs.",
|
||||
"s/ / /g",
|
||||
"I'm too foo for this bar",
|
||||
"Things went wrong...",
|
||||
"??! what the ...",
|
||||
"This solves it.",
|
||||
"Working on tests (haha)",
|
||||
"fixed conflicts (LOL merge -s ours; push -f)",
|
||||
"last minute fixes.",
|
||||
"fuckup.",
|
||||
"Revert \"fuckup\".",
|
||||
"should work now.",
|
||||
"final commit.",
|
||||
"done. going to bed now.",
|
||||
"buenas those-things.",
|
||||
"Your commit is writing checks your merge can't cash.",
|
||||
"This branch is so dirty, even your mom can't clean it.",
|
||||
"wip",
|
||||
"Revert \"just testing, remember to revert\"",
|
||||
"bla",
|
||||
"harharhar",
|
||||
"restored deleted entities just to be sure",
|
||||
"added some filthy stuff",
|
||||
"bugger",
|
||||
"lol",
|
||||
"oopsie B|",
|
||||
"Copy pasta fail. still had a instead of a",
|
||||
"Now added delete for real",
|
||||
"grmbl",
|
||||
"move your body every every body",
|
||||
"Trying to fake a conflict",
|
||||
"And a commit that I don't know the reason of...",
|
||||
"ffs",
|
||||
"that's all folks",
|
||||
"Fucking submodule bull shit",
|
||||
"apparently i did something…",
|
||||
"bump to 0.0.3-dev:wq",
|
||||
"pep8 - cause I fell like doing a barrel roll",
|
||||
"pep8 fixer",
|
||||
"it is hump day _^_",
|
||||
"happy monday _ bleh _",
|
||||
"after of this commit remember do a git reset hard",
|
||||
"someday I gonna kill someone for this shit...",
|
||||
"magic, have no clue but it works",
|
||||
"I am sorry",
|
||||
"dirty hack, have a better idea ?",
|
||||
"Code was clean until manager requested to fuck it up",
|
||||
" - Temporary commit.",
|
||||
":(:(",
|
||||
"...",
|
||||
"GIT :/",
|
||||
"stopped caring 10 commits ago",
|
||||
"Testing in progress ;)",
|
||||
"Fixed Bug",
|
||||
"Fixed errors",
|
||||
"Push poorly written test can down the road another ten years",
|
||||
"commented out failing tests",
|
||||
"I'm human",
|
||||
"TODO: write meaningful commit message",
|
||||
"Pig",
|
||||
"SOAP is a piece of shit",
|
||||
"did everything",
|
||||
"project lead is allergic to changes...",
|
||||
"making this thing actually usable.",
|
||||
"I was told to leave it alone, but I have this thing called OCD, you see",
|
||||
"Whatever will be, will be 8{",
|
||||
"It's 2015; why are we using ColdFusion?!",
|
||||
"#GrammarNazi",
|
||||
"Future self, please forgive me and don't hit me with the baseball bat again!",
|
||||
"Hide those navs, boi!",
|
||||
"Who knows...",
|
||||
"Who knows WTF?!",
|
||||
"I should get a raise for this.",
|
||||
"Done, to whoever merges this, good luck.",
|
||||
"Not one conflict, today was a good day.",
|
||||
"First Blood",
|
||||
"Fixed the fuck out of #526!",
|
||||
"I'm too old for this shit!",
|
||||
"One little whitespace gets its very own commit! Oh, life is so erratic!",
|
||||
"please dont let this be the problem",
|
||||
"good: no crash. bad: nothing happens",
|
||||
"trying",
|
||||
"trying harder",
|
||||
"i tried",
|
||||
"fml"
|
||||
}
|
||||
|
||||
function commit:action(msg)
|
||||
|
||||
local output = '`'..commits[math.random(#commits)]..'`'
|
||||
utilities.send_message(self, msg.chat.id, output, true, nil, true)
|
||||
|
||||
bindings.request(
|
||||
self,
|
||||
'sendMessage',
|
||||
{
|
||||
chat_id = msg.chat.id,
|
||||
text = '```\n' .. (http.request('http://whatthecommit.com/index.txt')) .. '\n```',
|
||||
parse_mode = 'Markdown'
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
return commit
|
||||
|
@ -11,14 +11,14 @@ end
|
||||
|
||||
function echo:action(msg)
|
||||
|
||||
local input = utilities.input(msg.text)
|
||||
local input = utilities.input_from_msg(msg)
|
||||
|
||||
if not input then
|
||||
utilities.send_message(self, msg.chat.id, echo.doc, true, msg.message_id, true)
|
||||
else
|
||||
local output
|
||||
if msg.chat.type == 'supergroup' then
|
||||
output = '*Echo:*\n"' .. utilities.md_escape(input) .. '"'
|
||||
output = utilities.style.enquote('Echo', input)
|
||||
else
|
||||
output = utilities.md_escape(utilities.char.zwnj..input)
|
||||
end
|
||||
|
@ -6,11 +6,10 @@ local utilities = require('otouto.utilities')
|
||||
|
||||
function fortune:init(config)
|
||||
local s = io.popen('fortune'):read('*all')
|
||||
if s:match('not found$') then
|
||||
print('fortune is not installed on this computer.')
|
||||
print('fortune.lua will not be enabled.')
|
||||
return
|
||||
end
|
||||
assert(
|
||||
not s:match('not found$'),
|
||||
'fortune.lua requires the fortune program to be installed.'
|
||||
)
|
||||
|
||||
fortune.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('fortune').table
|
||||
end
|
||||
|
@ -9,37 +9,28 @@ local JSON = require('dkjson')
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
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 config.google_cse_key then
|
||||
print('Missing config value: google_cse_key.')
|
||||
print('gImages.lua will not be enabled.')
|
||||
return
|
||||
end
|
||||
assert(config.google_api_key and config.google_cse_key,
|
||||
'gImages.lua requires a Google API key from http://console.developers.google.com and a Google Custom Search Engine key from http://cse.google.com/cse.'
|
||||
)
|
||||
|
||||
gImages.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('image', true):t('i', true):t('insfw', true).table
|
||||
gImages.doc = config.cmd_pat .. [[image <query>
|
||||
Returns a randomized top result from Google Images. Safe search is enabled by default; use "]] .. config.cmd_pat .. [[insfw" to disable it. NSFW results will not display an image preview.
|
||||
Alias: ]] .. config.cmd_pat .. 'i'
|
||||
gImages.search_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
|
||||
end
|
||||
|
||||
gImages.command = 'image <query>'
|
||||
|
||||
function gImages:action(msg, config)
|
||||
|
||||
local input = utilities.input(msg.text)
|
||||
local input = utilities.input_from_msg(msg)
|
||||
if not input then
|
||||
if msg.reply_to_message and msg.reply_to_message.text then
|
||||
input = msg.reply_to_message.text
|
||||
else
|
||||
utilities.send_message(self, msg.chat.id, gImages.doc, true, msg.message_id, true)
|
||||
return
|
||||
end
|
||||
utilities.send_reply(self, msg, gImages.doc, true)
|
||||
return
|
||||
end
|
||||
|
||||
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
|
||||
local url = gImages.search_url
|
||||
|
||||
if not string.match(msg.text, '^'..config.cmd_pat..'i[mage]*nsfw') then
|
||||
url = url .. '&safe=high'
|
||||
|
@ -6,28 +6,26 @@ local utilities = require('otouto.utilities')
|
||||
gMaps.command = 'location <query>'
|
||||
|
||||
function gMaps:init(config)
|
||||
gMaps.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('location', true):t('loc', true).table
|
||||
gMaps.doc = config.cmd_pat .. [[location <query>
|
||||
gMaps.triggers = utilities.triggers(self.info.username, config.cmd_pat)
|
||||
:t('location', true):t('loc', true).table
|
||||
gMaps.doc = [[
|
||||
/location <query>
|
||||
Returns a location from Google Maps.
|
||||
Alias: ]] .. config.cmd_pat .. 'loc'
|
||||
Alias: /loc
|
||||
]]
|
||||
gMaps.doc = gMaps.doc:gsub('/', config.cmd_pat)
|
||||
end
|
||||
|
||||
function gMaps:action(msg, config)
|
||||
|
||||
local input = utilities.input(msg.text)
|
||||
local input = utilities.input_from_msg(msg)
|
||||
if not input then
|
||||
if msg.reply_to_message and msg.reply_to_message.text then
|
||||
input = msg.reply_to_message.text
|
||||
else
|
||||
utilities.send_message(self, msg.chat.id, gMaps.doc, true, msg.message_id, true)
|
||||
return
|
||||
end
|
||||
utilities.send_reply(self, msg, gMaps.doc, true)
|
||||
return
|
||||
end
|
||||
|
||||
local coords = utilities.get_coords(input, config)
|
||||
if type(coords) == 'string' then
|
||||
utilities.send_reply(self, msg, coords)
|
||||
return
|
||||
end
|
||||
|
||||
bindings.sendLocation(self, {
|
||||
@ -36,7 +34,6 @@ function gMaps:action(msg, config)
|
||||
longitude = coords.lon,
|
||||
reply_to_message_id = msg.message_id
|
||||
} )
|
||||
|
||||
end
|
||||
|
||||
return gMaps
|
||||
|
@ -1,79 +0,0 @@
|
||||
local gSearch = {}
|
||||
|
||||
local HTTPS = require('ssl.https')
|
||||
local URL = require('socket.url')
|
||||
local JSON = require('dkjson')
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
gSearch.command = 'google <query>'
|
||||
|
||||
function gSearch:init(config)
|
||||
gSearch.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('g', true):t('google', true):t('gnsfw', true).table
|
||||
gSearch.doc = config.cmd_pat .. [[google <query>
|
||||
Returns four (if group) or eight (if private message) results from Google. Safe search is enabled by default, use "]] .. config.cmd_pat .. [[gnsfw" to disable it.
|
||||
Alias: ]] .. config.cmd_pat .. 'g'
|
||||
end
|
||||
|
||||
function gSearch:action(msg, config)
|
||||
|
||||
local input = utilities.input(msg.text)
|
||||
if not input then
|
||||
if msg.reply_to_message and msg.reply_to_message.text then
|
||||
input = msg.reply_to_message.text
|
||||
else
|
||||
utilities.send_message(self, msg.chat.id, gSearch.doc, true, msg.message_id, true)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local url = 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0'
|
||||
|
||||
if msg.from.id == msg.chat.id then
|
||||
url = url .. '&rsz=8'
|
||||
else
|
||||
url = url .. '&rsz=4'
|
||||
end
|
||||
|
||||
if not string.match(msg.text, '^'..config.cmd_pat..'g[oogle]*nsfw') then
|
||||
url = url .. '&safe=active'
|
||||
end
|
||||
|
||||
url = url .. '&q=' .. URL.escape(input)
|
||||
|
||||
local jstr, res = HTTPS.request(url)
|
||||
if res ~= 200 then
|
||||
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, config.errors.connection)
|
||||
return
|
||||
end
|
||||
if not jdat.responseData.results[1] then
|
||||
utilities.send_reply(self, msg, config.errors.results)
|
||||
return
|
||||
end
|
||||
|
||||
local output = '*Google results for* _' .. input .. '_ *:*\n'
|
||||
for i,_ in ipairs(jdat.responseData.results) do
|
||||
local title = jdat.responseData.results[i].titleNoFormatting:gsub('%[.+%]', ''):gsub('&', '&')
|
||||
--[[
|
||||
if title:len() > 48 then
|
||||
title = title:sub(1, 45) .. '...'
|
||||
end
|
||||
]]--
|
||||
local u = jdat.responseData.results[i].unescapedUrl
|
||||
if u:find('%)') then
|
||||
output = output .. '• ' .. title .. '\n' .. u:gsub('_', '\\_') .. '\n'
|
||||
else
|
||||
output = output .. '• [' .. title .. '](' .. u .. ')\n'
|
||||
end
|
||||
end
|
||||
|
||||
utilities.send_message(self, msg.chat.id, output, true, nil, true)
|
||||
|
||||
end
|
||||
|
||||
return gSearch
|
@ -1,63 +1,32 @@
|
||||
-- Put this on the bottom of your plugin list, after help.lua.
|
||||
-- If you want to configure your own greetings, copy the following table
|
||||
-- (without the "config.") to your config.lua file.
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
local greetings = {}
|
||||
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
function greetings:init(config)
|
||||
config.greetings = config.greetings or {
|
||||
['Hello, #NAME.'] = {
|
||||
'hello',
|
||||
'hey',
|
||||
'sup',
|
||||
'hi',
|
||||
'good morning',
|
||||
'good day',
|
||||
'good afternoon',
|
||||
'good evening'
|
||||
},
|
||||
['Goodbye, #NAME.'] = {
|
||||
'bye',
|
||||
'later',
|
||||
'see ya',
|
||||
'good night'
|
||||
},
|
||||
['Welcome back, #NAME.'] = {
|
||||
'i\'m home',
|
||||
'i\'m back'
|
||||
},
|
||||
['You\'re welcome, #NAME.'] = {
|
||||
'thanks',
|
||||
'thank you'
|
||||
}
|
||||
}
|
||||
|
||||
greetings.triggers = {
|
||||
self.info.first_name:lower() .. '%p*$'
|
||||
}
|
||||
greetings.triggers = {}
|
||||
for _, triggers in pairs(config.greetings) do
|
||||
for i = 1, #triggers do
|
||||
triggers[i] = '^' .. triggers[i] .. ',? ' .. self.info.first_name:lower() .. '%p*$'
|
||||
table.insert(greetings.triggers, triggers[i])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function greetings:action(msg, config)
|
||||
|
||||
local nick = utilities.build_name(msg.from.first_name, msg.from.last_name)
|
||||
local nick
|
||||
if self.database.userdata[tostring(msg.from.id)] then
|
||||
nick = self.database.userdata[tostring(msg.from.id)].nickname or nick
|
||||
nick = self.database.userdata[tostring(msg.from.id)].nickname
|
||||
end
|
||||
nick = nick or utilities.build_name(msg.from.first_name, msg.from.last_name)
|
||||
|
||||
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
|
||||
local output = utilities.char.zwnj .. trigger:gsub('#NAME', nick)
|
||||
utilities.send_message(self, msg.chat.id, output)
|
||||
for response, triggers in pairs(config.greetings) do
|
||||
for _, trigger in pairs(triggers) do
|
||||
if string.match(msg.text_lower, trigger) then
|
||||
utilities.send_message(self, msg.chat.id, response:gsub('#NAME', nick))
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
|
||||
end
|
||||
|
||||
return greetings
|
||||
|
@ -1,63 +1,75 @@
|
||||
local hackernews = {}
|
||||
|
||||
local HTTPS = require('ssl.https')
|
||||
local JSON = require('dkjson')
|
||||
local bindings = require('otouto.bindings')
|
||||
local utilities = require('otouto.utilities')
|
||||
local bindings = require('otouto.bindings')
|
||||
|
||||
local hackernews = {}
|
||||
|
||||
hackernews.command = 'hackernews'
|
||||
|
||||
local function get_hackernews_results()
|
||||
local results = {}
|
||||
local jstr, code = HTTPS.request(hackernews.topstories_url)
|
||||
if code ~= 200 then return end
|
||||
local data = JSON.decode(jstr)
|
||||
for i = 1, 8 do
|
||||
local ijstr, icode = HTTPS.request(hackernews.res_url:format(data[i]))
|
||||
if icode ~= 200 then return end
|
||||
local idata = JSON.decode(ijstr)
|
||||
local result
|
||||
if idata.url then
|
||||
result = string.format(
|
||||
'\n• <code>[</code><a href="%s">%s</a><code>]</code> <a href="%s">%s</a>',
|
||||
utilities.html_escape(hackernews.art_url:format(idata.id)),
|
||||
idata.id,
|
||||
utilities.html_escape(idata.url),
|
||||
utilities.html_escape(idata.title)
|
||||
)
|
||||
else
|
||||
result = string.format(
|
||||
'\n• <code>[</code><a href="%s">%s</a><code>]</code> %s',
|
||||
utilities.html_escape(hackernews.art_url:format(idata.id)),
|
||||
idata.id,
|
||||
utilities.html_escape(idata.title)
|
||||
)
|
||||
end
|
||||
table.insert(results, result)
|
||||
end
|
||||
return results
|
||||
end
|
||||
|
||||
function hackernews:init(config)
|
||||
hackernews.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('hackernews', true):t('hn', true).table
|
||||
hackernews.doc = [[Returns four (if group) or eight (if private message) top stories from Hacker News.
|
||||
Alias: ]] .. config.cmd_pat .. 'hn'
|
||||
hackernews.topstories_url = 'https://hacker-news.firebaseio.com/v0/topstories.json'
|
||||
hackernews.res_url = 'https://hacker-news.firebaseio.com/v0/item/%s.json'
|
||||
hackernews.art_url = 'https://news.ycombinator.com/item?id=%s'
|
||||
hackernews.last_update = 0
|
||||
if config.hackernews_onstart == true then
|
||||
hackernews.results = get_hackernews_results()
|
||||
if hackernews.results then hackernews.last_update = os.time() / 60 end
|
||||
end
|
||||
end
|
||||
|
||||
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, config.errors.connection)
|
||||
return
|
||||
local now = os.time() / 60
|
||||
if not hackernews.results or hackernews.last_update + config.hackernews_interval < now then
|
||||
bindings.sendChatAction(self, { chat_id = msg.chat.id, action = 'typing' })
|
||||
hackernews.results = get_hackernews_results()
|
||||
if not hackernews.results then
|
||||
utilities.send_reply(self, msg, config.errors.connection)
|
||||
return
|
||||
end
|
||||
hackernews.last_update = now
|
||||
end
|
||||
|
||||
local jdat = JSON.decode(jstr)
|
||||
|
||||
local res_count = 4
|
||||
if msg.chat.id == msg.from.id then
|
||||
res_count = 8
|
||||
end
|
||||
|
||||
local output = '*Hacker News:*\n'
|
||||
-- Four results in a group, eight in private.
|
||||
local res_count = msg.chat.id == msg.from.id and 8 or 4
|
||||
local output = '<b>Top Stories from Hacker News:</b>'
|
||||
for i = 1, res_count do
|
||||
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, config.errors.connection)
|
||||
return
|
||||
end
|
||||
local res_jdat = JSON.decode(jstr)
|
||||
local title = res_jdat.title:gsub('%[.+%]', ''):gsub('%(.+%)', ''):gsub('&', '&')
|
||||
if title:len() > 48 then
|
||||
title = title:sub(1, 45) .. '...'
|
||||
end
|
||||
local url = res_jdat.url
|
||||
if not url then
|
||||
utilities.send_reply(self, msg, config.errors.connection)
|
||||
return
|
||||
end
|
||||
if url:find('%(') then
|
||||
output = output .. '• ' .. title .. '\n' .. url:gsub('_', '\\_') .. '\n'
|
||||
else
|
||||
output = output .. '• [' .. title .. '](' .. url .. ')\n'
|
||||
end
|
||||
|
||||
output = output .. hackernews.results[i]
|
||||
end
|
||||
|
||||
utilities.send_message(self, msg.chat.id, output, true, nil, true)
|
||||
|
||||
utilities.send_message(self, msg.chat.id, output, true, nil, 'html')
|
||||
end
|
||||
|
||||
return hackernews
|
||||
|
@ -5,45 +5,36 @@ local hearthstone = {}
|
||||
--local HTTPS = require('ssl.https')
|
||||
local JSON = require('dkjson')
|
||||
local utilities = require('otouto.utilities')
|
||||
local HTTPS = require('ssl.https')
|
||||
|
||||
function hearthstone:init(config)
|
||||
hearthstone.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('hearthstone', true):t('hs').table
|
||||
hearthstone.command = 'hearthstone <query>'
|
||||
|
||||
if not self.database.hearthstone or os.time() > self.database.hearthstone.expiration then
|
||||
|
||||
print('Downloading Hearthstone database...')
|
||||
|
||||
-- This stuff doesn't play well with lua-sec. Disable it for now; hack in curl.
|
||||
--local jstr, res = HTTPS.request('https://api.hearthstonejson.com/v1/latest/enUS/cards.json')
|
||||
--if res ~= 200 then
|
||||
-- print('Error connecting to hearthstonejson.com.')
|
||||
-- print('hearthstone.lua will not be enabled.')
|
||||
-- return
|
||||
--end
|
||||
--local jdat = JSON.decode(jstr)
|
||||
|
||||
local s = io.popen('curl -s https://api.hearthstonejson.com/v1/latest/enUS/cards.json'):read('*all')
|
||||
local d = JSON.decode(s)
|
||||
|
||||
if not d then
|
||||
local jstr, res = HTTPS.request('https://api.hearthstonejson.com/v1/latest/enUS/cards.json')
|
||||
if not jstr or res ~= 200 then
|
||||
print('Error connecting to hearthstonejson.com.')
|
||||
print('hearthstone.lua will not be enabled.')
|
||||
hearthstone.command = nil
|
||||
hearthstone.triggers = nil
|
||||
return
|
||||
end
|
||||
|
||||
self.database.hearthstone = d
|
||||
self.database.hearthstone = JSON.decode(jstr)
|
||||
self.database.hearthstone.expiration = os.time() + 600000
|
||||
|
||||
print('Download complete! It will be stored for a week.')
|
||||
|
||||
end
|
||||
|
||||
hearthstone.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('hearthstone', true):t('hs').table
|
||||
hearthstone.doc = config.cmd_pat .. [[hearthstone <query>
|
||||
Returns Hearthstone card info.
|
||||
Alias: ]] .. config.cmd_pat .. 'hs'
|
||||
end
|
||||
|
||||
hearthstone.command = 'hearthstone <query>'
|
||||
|
||||
local function format_card(card)
|
||||
|
||||
local ctype = card.type
|
||||
@ -102,9 +93,9 @@ end
|
||||
|
||||
function hearthstone:action(msg, config)
|
||||
|
||||
local input = utilities.input(msg.text_lower)
|
||||
local input = utilities.input_from_msg(msg)
|
||||
if not input then
|
||||
utilities.send_message(self, msg.chat.id, hearthstone.doc, true, msg.message_id, true)
|
||||
utilities.send_reply(self, msg, hearthstone.doc, true)
|
||||
return
|
||||
end
|
||||
|
||||
|
@ -1,46 +1,47 @@
|
||||
-- This plugin should go at the end of your plugin list in
|
||||
-- config.lua, but not after greetings.lua.
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
local help = {}
|
||||
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
local help_text
|
||||
|
||||
function help:init(config)
|
||||
local commandlist = {}
|
||||
help_text = '*Available commands:*\n• '..config.cmd_pat
|
||||
for _,plugin in ipairs(self.plugins) do
|
||||
if plugin.command then
|
||||
table.insert(commandlist, plugin.command)
|
||||
if plugin.doc then
|
||||
plugin.help_word = utilities.get_word(plugin.command, 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
table.insert(commandlist, 'help [command]')
|
||||
table.sort(commandlist)
|
||||
help_text = help_text .. table.concat(commandlist, '\n• '..config.cmd_pat) .. '\nArguments: <required> [optional]'
|
||||
help_text = help_text:gsub('%[', '\\[')
|
||||
help.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('help', true):t('h', true).table
|
||||
help.command = 'help [command]'
|
||||
help.doc = config.cmd_pat .. 'help [command] \nReturns usage information for a given command.'
|
||||
end
|
||||
|
||||
function help:action(msg)
|
||||
function help:action(msg, config)
|
||||
local input = utilities.input(msg.text_lower)
|
||||
if input then
|
||||
if not help.help_word then
|
||||
for _, plugin in ipairs(self.plugins) do
|
||||
if plugin.command and plugin.doc and not plugin.help_word then
|
||||
plugin.help_word = utilities.get_word(plugin.command, 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
for _,plugin in ipairs(self.plugins) do
|
||||
if plugin.help_word == input:gsub('^/', '') then
|
||||
local output = '*Help for* _' .. plugin.help_word .. '_ *:*\n' .. plugin.doc
|
||||
local output = '*Help for* _' .. plugin.help_word .. '_*:*\n' .. plugin.doc
|
||||
utilities.send_message(self, msg.chat.id, output, true, nil, true)
|
||||
return
|
||||
end
|
||||
end
|
||||
utilities.send_reply(self, msg, 'Sorry, there is no help for that command.')
|
||||
else
|
||||
-- Generate the help message on first run.
|
||||
if not help.text then
|
||||
local commandlist = {}
|
||||
for _, plugin in ipairs(self.plugins) do
|
||||
if plugin.command then
|
||||
table.insert(commandlist, plugin.command)
|
||||
end
|
||||
end
|
||||
table.sort(commandlist)
|
||||
help.text = '*Available commands:*\n• ' .. config.cmd_pat .. table.concat(commandlist, '\n• '..config.cmd_pat) .. '\nArguments: <required> [optional]'
|
||||
help.text = help.text:gsub('%[', '\\[')
|
||||
end
|
||||
-- Attempt to send the help message via PM.
|
||||
-- If msg is from a group, tell the group whether the PM was successful.
|
||||
local res = utilities.send_message(self, msg.from.id, help_text, true, nil, true)
|
||||
local res = utilities.send_message(self, msg.from.id, help.text, true, nil, true)
|
||||
if not res then
|
||||
utilities.send_reply(self, msg, 'Please [message me privately](http://telegram.me/' .. self.info.username .. '?start=help) for a list of commands.', true)
|
||||
elseif msg.chat.type ~= 'private' then
|
||||
|
62
otouto/plugins/id.lua
Normal file
62
otouto/plugins/id.lua
Normal file
@ -0,0 +1,62 @@
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
local id = {}
|
||||
|
||||
function id:init(config)
|
||||
id.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('id', true).table
|
||||
id.command = 'id <user>'
|
||||
id.doc = config.cmd_pat .. [[id <user> ...
|
||||
Returns the name, ID, and username (if applicable) for the given users.
|
||||
Arguments must be usernames and/or IDs. Input is also accepted via reply. If no input is given, returns info for the user.
|
||||
]]
|
||||
end
|
||||
|
||||
function id.format(t)
|
||||
if t.username then
|
||||
return string.format(
|
||||
'@%s, AKA <b>%s</b> <code>[%s]</code>.\n',
|
||||
t.username,
|
||||
utilities.build_name(t.first_name, t.last_name),
|
||||
t.id
|
||||
)
|
||||
else
|
||||
return string.format(
|
||||
'<b>%s</b> <code>[%s]</code>.\n',
|
||||
utilities.build_name(t.first_name, t.last_name),
|
||||
t.id
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
function id:action(msg)
|
||||
local output
|
||||
local input = utilities.input(msg.text)
|
||||
if msg.reply_to_message then
|
||||
output = id.format(msg.reply_to_message.from)
|
||||
elseif input then
|
||||
output = ''
|
||||
for user in input:gmatch('%g+') do
|
||||
if tonumber(user) then
|
||||
if self.database.users[user] then
|
||||
output = output .. id.format(self.database.users[user])
|
||||
else
|
||||
output = output .. 'I don\'t recognize that ID (' .. user .. ').\n'
|
||||
end
|
||||
elseif user:match('^@') then
|
||||
local t = utilities.resolve_username(self, user)
|
||||
if t then
|
||||
output = output .. id.format(t)
|
||||
else
|
||||
output = output .. 'I don\'t recognize that username (' .. user .. ').\n'
|
||||
end
|
||||
else
|
||||
output = output .. 'Invalid username or ID (' .. user .. ').\n'
|
||||
end
|
||||
end
|
||||
else
|
||||
output = id.format(msg.from)
|
||||
end
|
||||
utilities.send_reply(self, msg, output, 'html')
|
||||
end
|
||||
|
||||
return id
|
@ -14,14 +14,10 @@ end
|
||||
|
||||
function imdb:action(msg, config)
|
||||
|
||||
local input = utilities.input(msg.text)
|
||||
local input = utilities.input_from_msg(msg)
|
||||
if not input then
|
||||
if msg.reply_to_message and msg.reply_to_message.text then
|
||||
input = msg.reply_to_message.text
|
||||
else
|
||||
utilities.send_message(self, msg.chat.id, imdb.doc, true, msg.message_id, true)
|
||||
return
|
||||
end
|
||||
utilities.send_reply(self, msg, imdb.doc, true)
|
||||
return
|
||||
end
|
||||
|
||||
local url = 'http://www.omdbapi.com/?t=' .. URL.escape(input)
|
||||
|
43
otouto/plugins/isup.lua
Normal file
43
otouto/plugins/isup.lua
Normal file
@ -0,0 +1,43 @@
|
||||
-- Based on a plugin by matthewhesketh.
|
||||
|
||||
local HTTP = require('socket.http')
|
||||
local HTTPS = require('ssl.https')
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
local isup = {}
|
||||
|
||||
function isup:init(config)
|
||||
isup.triggers = utilities.triggers(self.info.username, config.cmd_pat)
|
||||
:t('websitedown', true):t('isitup', true):t('isup', true).table
|
||||
|
||||
isup.doc = config.cmd_pat .. [[isup <url>
|
||||
Returns the up or down status of a website.]]
|
||||
isup.command = 'isup <url>'
|
||||
end
|
||||
|
||||
function isup:action(msg, config)
|
||||
local input = utilities.input_from_msg(msg)
|
||||
if not input then
|
||||
utilities.send_reply(self, msg, isup.doc)
|
||||
return
|
||||
end
|
||||
|
||||
local protocol = HTTP
|
||||
local url_lower = input:lower()
|
||||
if url_lower:match('^https') then
|
||||
protocol = HTTPS
|
||||
elseif not url_lower:match('^http') then
|
||||
input = 'http://' .. input
|
||||
end
|
||||
local _, code = protocol.request(input)
|
||||
code = tonumber(code)
|
||||
local output
|
||||
if not code or code > 399 then
|
||||
output = 'This website is down or nonexistent.'
|
||||
else
|
||||
output = 'This website is up.'
|
||||
end
|
||||
utilities.send_reply(self, msg, output, true)
|
||||
end
|
||||
|
||||
return isup
|
@ -9,11 +9,9 @@ local JSON = require('dkjson')
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
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
|
||||
end
|
||||
assert(config.lastfm_api_key,
|
||||
'lastfm.lua requires a last.fm API key from http://last.fm/api.'
|
||||
)
|
||||
|
||||
lastfm.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('lastfm', true):t('np', true):t('fmset', true).table
|
||||
lastfm.doc = config.cmd_pat .. [[np [username]
|
||||
|
@ -2,10 +2,21 @@ local luarun = {}
|
||||
|
||||
local utilities = require('otouto.utilities')
|
||||
local URL = require('socket.url')
|
||||
local JSON = require('dkjson')
|
||||
local JSON, serpent
|
||||
|
||||
function luarun:init(config)
|
||||
luarun.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('lua', true):t('return', true).table
|
||||
if config.luarun_serpent then
|
||||
serpent = require('serpent')
|
||||
luarun.serialize = function(t)
|
||||
return serpent.block(t, {comment=false})
|
||||
end
|
||||
else
|
||||
JSON = require('dkjson')
|
||||
luarun.serialize = function(t)
|
||||
return JSON.encode(t, {indent=true})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function luarun:action(msg, config)
|
||||
@ -28,6 +39,7 @@ function luarun:action(msg, config)
|
||||
local bot = require('otouto.bot')
|
||||
local bindings = require('otouto.bindings')
|
||||
local utilities = require('otouto.utilities')
|
||||
local drua = require('otouto.drua-tg')
|
||||
local JSON = require('dkjson')
|
||||
local URL = require('socket.url')
|
||||
local HTTP = require('socket.http')
|
||||
@ -38,7 +50,7 @@ function luarun:action(msg, config)
|
||||
output = 'Done!'
|
||||
else
|
||||
if type(output) == 'table' then
|
||||
local s = JSON.encode(output, {indent=true})
|
||||
local s = luarun.serialize(output)
|
||||
if URL.escape(s):len() < 4000 then
|
||||
output = s
|
||||
end
|
||||
|
@ -9,33 +9,59 @@ function me:init(config)
|
||||
end
|
||||
|
||||
function me:action(msg, config)
|
||||
|
||||
local userdata = self.database.userdata[tostring(msg.from.id)] or {}
|
||||
|
||||
local user
|
||||
if msg.from.id == config.admin then
|
||||
if msg.reply_to_message then
|
||||
userdata = self.database.userdata[tostring(msg.reply_to_message.from.id)]
|
||||
user = msg.reply_to_message.from
|
||||
else
|
||||
local input = utilities.input(msg.text)
|
||||
if input then
|
||||
local user_id = utilities.id_from_username(self, input)
|
||||
if user_id then
|
||||
userdata = self.database.userdata[tostring(user_id)] or {}
|
||||
if tonumber(input) then
|
||||
user = self.database.users[input]
|
||||
if not user then
|
||||
utilities.send_reply(self, msg, 'Unrecognized ID.')
|
||||
return
|
||||
end
|
||||
elseif input:match('^@') then
|
||||
user = utilities.resolve_username(self, input)
|
||||
if not user then
|
||||
utilities.send_reply(self, msg, 'Unrecognized username.')
|
||||
return
|
||||
end
|
||||
else
|
||||
utilities.send_reply(self, msg, 'Invalid username or ID.')
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
user = user or msg.from
|
||||
local userdata = self.database.userdata[tostring(user.id)] or {}
|
||||
|
||||
local output = ''
|
||||
local data = {}
|
||||
for k,v in pairs(userdata) do
|
||||
output = output .. '*' .. k .. ':* `' .. tostring(v) .. '`\n'
|
||||
table.insert(data, string.format(
|
||||
'<b>%s</b> <code>%s</code>\n',
|
||||
utilities.html_escape(k),
|
||||
utilities.html_escape(v)
|
||||
))
|
||||
end
|
||||
|
||||
if output == '' then
|
||||
local output
|
||||
if #data == 0 then
|
||||
output = 'There is no data stored for this user.'
|
||||
else
|
||||
output = string.format(
|
||||
'<b>%s</b> <code>[%s]</code><b>:</b>\n',
|
||||
utilities.html_escape(utilities.build_name(
|
||||
user.first_name,
|
||||
user.last_name
|
||||
)),
|
||||
user.id
|
||||
) .. table.concat(data)
|
||||
end
|
||||
|
||||
utilities.send_message(self, msg.chat.id, output, true, nil, true)
|
||||
utilities.send_message(self, msg.chat.id, output, true, nil, 'html')
|
||||
|
||||
end
|
||||
|
||||
|
@ -1,10 +1,18 @@
|
||||
local patterns = {}
|
||||
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
patterns.triggers = {
|
||||
'^/?s/.-/.-$'
|
||||
}
|
||||
local patterns = {}
|
||||
|
||||
patterns.command = 's/<pattern>/<substitution>'
|
||||
patterns.help_word = 'sed'
|
||||
patterns.doc = [[
|
||||
s/<pattern>/<substitution>
|
||||
Replace all matches for the given pattern.
|
||||
Uses Lua patterns.
|
||||
]]
|
||||
|
||||
function patterns:init(config)
|
||||
patterns.triggers = { config.cmd_pat .. '?s/.-/.-$' }
|
||||
end
|
||||
|
||||
function patterns:action(msg)
|
||||
if not msg.reply_to_message then return true end
|
||||
@ -24,8 +32,8 @@ function patterns:action(msg)
|
||||
if res == false then
|
||||
utilities.send_reply(self, msg, 'Malformed pattern!')
|
||||
else
|
||||
output = output:sub(1, 4000)
|
||||
output = '*Did you mean:*\n"' .. utilities.md_escape(utilities.trim(output)) .. '"'
|
||||
output = utilities.trim(output:sub(1, 4000))
|
||||
output = utilities.style.enquote('Did you mean', output)
|
||||
utilities.send_reply(self, msg.reply_to_message, output, true)
|
||||
end
|
||||
end
|
||||
|
@ -11,23 +11,20 @@ function pokedex:init(config)
|
||||
pokedex.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('pokedex', true):t('dex', true).table
|
||||
pokedex.doc = config.cmd_pat .. [[pokedex <query>
|
||||
Returns a Pokedex entry from pokeapi.co.
|
||||
Queries must be a number of the name of a Pokémon.
|
||||
Alias: ]] .. config.cmd_pat .. 'dex'
|
||||
end
|
||||
|
||||
function pokedex:action(msg, config)
|
||||
|
||||
bindings.sendChatAction(self, { chat_id = msg.chat.id, action = 'typing' } )
|
||||
|
||||
local input = utilities.input(msg.text_lower)
|
||||
local input = utilities.input_from_msg(msg)
|
||||
if not input then
|
||||
if msg.reply_to_message and msg.reply_to_message.text then
|
||||
input = msg.reply_to_message.text
|
||||
else
|
||||
utilities.send_message(self, msg.chat.id, pokedex.doc, true, msg.message_id, true)
|
||||
return
|
||||
end
|
||||
utilities.send_reply(self, msg, pokedex.doc, true)
|
||||
return
|
||||
end
|
||||
|
||||
bindings.sendChatAction(self, { chat_id = msg.chat.id, action = 'typing' } )
|
||||
|
||||
local url = 'http://pokeapi.co'
|
||||
|
||||
local dex_url = url .. '/api/v1/pokemon/' .. input
|
||||
@ -39,6 +36,11 @@ function pokedex:action(msg, config)
|
||||
|
||||
local dex_jdat = JSON.decode(dex_jstr)
|
||||
|
||||
if not dex_jdat.descriptions or not dex_jdat.descriptions[1] then
|
||||
utilities.send_reply(self, msg, config.errors.results)
|
||||
return
|
||||
end
|
||||
|
||||
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
|
||||
|
@ -97,8 +97,12 @@ function pgc:action(msg)
|
||||
if egg_count < 1 then
|
||||
recommendation = 'Wait until you have atleast sixty Pokémon to evolve before using a lucky egg.'
|
||||
else
|
||||
recommendation = 'Use %s lucky egg(s) for %s evolutions.'
|
||||
recommendation = recommendation:format(egg_count, egg_count*60)
|
||||
recommendation = string.format(
|
||||
'Use %s lucky egg%s for %s evolutions.',
|
||||
egg_count,
|
||||
egg_count == 1 and '' or 's',
|
||||
egg_count * 60
|
||||
)
|
||||
end
|
||||
s = s:format(total_evolutions, recommendation)
|
||||
output = output .. s
|
||||
|
@ -8,7 +8,8 @@ pokemon_go.command = 'pokego <team>'
|
||||
function pokemon_go:init(config)
|
||||
pokemon_go.triggers = utilities.triggers(self.info.username, config.cmd_pat)
|
||||
:t('pokego', true):t('pokégo', true)
|
||||
:t('pokemongo', true):t('pokémongo', true).table
|
||||
:t('pokemongo', true):t('pokémongo', true)
|
||||
:t('pogo', true):t('mongo', true).table
|
||||
pokemon_go.doc = config.cmd_pat .. [[pokego <team>
|
||||
Set your Pokémon Go team for statistical purposes. The team must be valid, and can be referred to by name or color (or the first letter of either). Giving no team name will show statistics.]]
|
||||
local db = self.database.pokemon_go
|
||||
|
@ -12,10 +12,9 @@ end
|
||||
|
||||
function preview:action(msg)
|
||||
|
||||
local input = utilities.input(msg.text)
|
||||
|
||||
local input = utilities.input_from_msg(msg)
|
||||
if not input then
|
||||
utilities.send_message(self, msg.chat.id, preview.doc, true, nil, true)
|
||||
utilities.send_reply(self, msg, preview.doc, true)
|
||||
return
|
||||
end
|
||||
|
||||
@ -36,8 +35,8 @@ function preview:action(msg)
|
||||
end
|
||||
|
||||
-- Invisible zero-width, non-joiner.
|
||||
local output = '[](' .. input .. ')'
|
||||
utilities.send_message(self, msg.chat.id, output, false, nil, true)
|
||||
local output = '<a href="' .. input .. '">' .. utilities.char.zwnj .. '</a>'
|
||||
utilities.send_message(self, msg.chat.id, output, false, nil, 'html')
|
||||
|
||||
end
|
||||
|
||||
|
@ -13,16 +13,6 @@ local utilities = require('otouto.utilities')
|
||||
reactions.command = 'reactions'
|
||||
reactions.doc = 'Returns a list of "reaction" emoticon commands.'
|
||||
|
||||
local mapping = {
|
||||
['shrug'] = '¯\\_(ツ)_/¯',
|
||||
['lenny'] = '( ͡° ͜ʖ ͡°)',
|
||||
['flip'] = '(╯°□°)╯︵ ┻━┻',
|
||||
['homo'] = '┌(┌ ^o^)┐',
|
||||
['look'] = 'ಠ_ಠ',
|
||||
['shots?'] = 'SHOTS FIRED',
|
||||
['facepalm'] = '(-‸ლ)'
|
||||
}
|
||||
|
||||
local help
|
||||
|
||||
function reactions:init(config)
|
||||
@ -30,8 +20,8 @@ function reactions:init(config)
|
||||
help = 'Reactions:\n'
|
||||
reactions.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('reactions').table
|
||||
local username = self.info.username:lower()
|
||||
for trigger,reaction in pairs(mapping) do
|
||||
help = help .. '• ' .. config.cmd_pat .. trigger:gsub('.%?', '') .. ': ' .. reaction .. '\n'
|
||||
for trigger,reaction in pairs(config.reactions) do
|
||||
help = help .. '• ' .. config.cmd_pat .. trigger .. ': ' .. reaction .. '\n'
|
||||
table.insert(reactions.triggers, '^'..config.cmd_pat..trigger)
|
||||
table.insert(reactions.triggers, '^'..config.cmd_pat..trigger..'@'..username)
|
||||
table.insert(reactions.triggers, config.cmd_pat..trigger..'$')
|
||||
@ -48,7 +38,7 @@ function reactions:action(msg, config)
|
||||
utilities.send_message(self, msg.chat.id, help)
|
||||
return
|
||||
end
|
||||
for trigger,reaction in pairs(mapping) do
|
||||
for trigger,reaction in pairs(config.reactions) do
|
||||
if string.match(msg.text_lower, config.cmd_pat..trigger) then
|
||||
utilities.send_message(self, msg.chat.id, reaction)
|
||||
return
|
||||
|
@ -8,87 +8,83 @@ function remind:init(config)
|
||||
self.database.reminders = self.database.reminders or {}
|
||||
|
||||
remind.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('remind', true).table
|
||||
remind.doc = config.cmd_pat .. 'remind <duration> <message> \nRepeats a message after a duration of time, in minutes.'
|
||||
|
||||
config.remind = config.remind or {}
|
||||
setmetatable(config.remind, { __index = function() return 1000 end })
|
||||
|
||||
remind.doc = config.cmd_pat .. [[remind <duration> <message>
|
||||
Repeats a message after a duration of time, in minutes.
|
||||
The maximum length of a reminder is %s characters. The maximum duration of a timer is %s minutes. The maximum number of reminders for a group is %s. The maximum number of reminders in private is %s.]]
|
||||
remind.doc = remind.doc:format(config.remind.max_length, config.remind.max_duration, config.remind.max_reminders_group, config.remind.max_reminders_private)
|
||||
end
|
||||
|
||||
function remind:action(msg)
|
||||
-- Ensure there are arguments. If not, send doc.
|
||||
function remind:action(msg, config)
|
||||
local input = utilities.input(msg.text)
|
||||
if not input then
|
||||
utilities.send_message(self, msg.chat.id, remind.doc, true, msg.message_id, true)
|
||||
utilities.send_reply(self, msg, remind.doc, true)
|
||||
return
|
||||
end
|
||||
-- Ensure first arg is a number. If not, send doc.
|
||||
local duration = utilities.get_word(input, 1)
|
||||
if not tonumber(duration) then
|
||||
utilities.send_message(self, msg.chat.id, remind.doc, true, msg.message_id, true)
|
||||
|
||||
local duration = tonumber(utilities.get_word(input, 1))
|
||||
if not duration then
|
||||
utilities.send_reply(self, msg, remind.doc, true)
|
||||
return
|
||||
end
|
||||
-- Duration must be between one minute and one year (approximately).
|
||||
duration = tonumber(duration)
|
||||
|
||||
if duration < 1 then
|
||||
duration = 1
|
||||
elseif duration > 526000 then
|
||||
duration = 526000
|
||||
elseif duration > config.remind.max_duration then
|
||||
duration = config.remind.max_duration
|
||||
end
|
||||
-- Ensure there is a second arg.
|
||||
local message = utilities.input(input)
|
||||
if not message then
|
||||
utilities.send_message(self, msg.chat.id, remind.doc, true, msg.message_id, true)
|
||||
utilities.send_reply(self, msg, remind.doc, true)
|
||||
return
|
||||
end
|
||||
|
||||
if #message > config.remind.max_length then
|
||||
utilities.send_reply(self, msg, 'The maximum length of reminders is ' .. config.remind.max_length .. '.')
|
||||
return
|
||||
end
|
||||
|
||||
local chat_id_str = tostring(msg.chat.id)
|
||||
-- Make a database entry for the group/user if one does not exist.
|
||||
local output
|
||||
self.database.reminders[chat_id_str] = self.database.reminders[chat_id_str] or {}
|
||||
-- Limit group reminders to 10 and private reminders to 50.
|
||||
if msg.chat.type ~= 'private' and utilities.table_size(self.database.reminders[chat_id_str]) > 9 then
|
||||
utilities.send_reply(self, msg, 'Sorry, this group already has ten reminders.')
|
||||
return
|
||||
elseif msg.chat.type == 'private' and utilities.table_size(self.database.reminders[chat_id_str]) > 49 then
|
||||
utilities.send_reply(msg, 'Sorry, you already have fifty reminders.')
|
||||
return
|
||||
end
|
||||
-- Put together the reminder with the expiration, message, and message to reply to.
|
||||
local reminder = {
|
||||
time = os.time() + duration * 60,
|
||||
message = message
|
||||
}
|
||||
table.insert(self.database.reminders[chat_id_str], reminder)
|
||||
local output = 'I will remind you in ' .. duration
|
||||
if duration == 1 then
|
||||
output = output .. ' minute!'
|
||||
if msg.chat.type == 'private' and utilities.table_size(self.database.reminders[chat_id_str]) >= config.remind.max_reminders_private then
|
||||
output = 'Sorry, you already have the maximum number of reminders.'
|
||||
elseif msg.chat.type ~= 'private' and utilities.table_size(self.database.reminders[chat_id_str]) >= config.remind.max_reminders_group then
|
||||
output = 'Sorry, this group already has the maximum number of reminders.'
|
||||
else
|
||||
output = output .. ' minutes!'
|
||||
table.insert(self.database.reminders[chat_id_str], {
|
||||
time = os.time() + (duration * 60),
|
||||
message = message
|
||||
})
|
||||
output = string.format(
|
||||
'I will remind you in %s minute%s!',
|
||||
duration,
|
||||
duration == 1 and '' or 's'
|
||||
)
|
||||
end
|
||||
utilities.send_reply(self, msg, output)
|
||||
utilities.send_reply(self, msg, output, true)
|
||||
end
|
||||
|
||||
function remind:cron()
|
||||
function remind:cron(config)
|
||||
local time = os.time()
|
||||
-- Iterate over the group entries in the reminders database.
|
||||
for chat_id, group in pairs(self.database.reminders) do
|
||||
local new_group = {}
|
||||
-- Iterate over each reminder.
|
||||
for _, reminder in ipairs(group) do
|
||||
for k, reminder in pairs(group) do
|
||||
-- If the reminder is past-due, send it and nullify it.
|
||||
-- Otherwise, add it to the replacement table.
|
||||
if time > reminder.time then
|
||||
local output = '*Reminder:*\n"' .. utilities.md_escape(reminder.message) .. '"'
|
||||
local output = utilities.style.enquote('Reminder', reminder.message)
|
||||
local res = utilities.send_message(self, chat_id, output, true, nil, true)
|
||||
-- If the message fails to send, save it for later.
|
||||
if not res then
|
||||
table.insert(new_group, reminder)
|
||||
-- If the message fails to send, save it for later (if enabled in config).
|
||||
if res or not config.remind.persist then
|
||||
group[k] = nil
|
||||
end
|
||||
else
|
||||
table.insert(new_group, reminder)
|
||||
end
|
||||
end
|
||||
-- Nullify the original table and replace it with the new one.
|
||||
self.database.reminders[chat_id] = new_group
|
||||
-- Nullify the table if it is empty.
|
||||
if #new_group == 0 then
|
||||
self.database.reminders[chat_id] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -20,7 +20,9 @@ function shell:action(msg, config)
|
||||
return
|
||||
end
|
||||
|
||||
local output = io.popen(input):read('*all')
|
||||
local f = io.popen(input)
|
||||
local output = f:read('*all')
|
||||
f:close()
|
||||
if output:len() == 0 then
|
||||
output = 'Done!'
|
||||
else
|
||||
|
@ -3,6 +3,7 @@ local shout = {}
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
shout.command = 'shout <text>'
|
||||
local utf8 = '('..utilities.char.utf_8..'*)'
|
||||
|
||||
function shout:init(config)
|
||||
shout.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('shout', true).table
|
||||
@ -11,22 +12,19 @@ end
|
||||
|
||||
function shout:action(msg)
|
||||
|
||||
local input = utilities.input(msg.text)
|
||||
local input = utilities.input_from_msg(msg)
|
||||
if not input then
|
||||
if msg.reply_to_message and #msg.reply_to_message.text > 0 then
|
||||
input = msg.reply_to_message.text
|
||||
else
|
||||
utilities.send_message(self, msg.chat.id, shout.doc, true, msg.message_id, true)
|
||||
return
|
||||
end
|
||||
utilities.send_reply(self, msg, shout.doc, true)
|
||||
return
|
||||
end
|
||||
|
||||
input = utilities.trim(input)
|
||||
input = input:upper()
|
||||
|
||||
local output = ''
|
||||
local inc = 0
|
||||
local ilen = 0
|
||||
for match in input:gmatch(utilities.char.utf_8) do
|
||||
for match in input:gmatch(utf8) do
|
||||
if ilen < 20 then
|
||||
ilen = ilen + 1
|
||||
output = output .. match .. ' '
|
||||
@ -34,7 +32,7 @@ function shout:action(msg)
|
||||
end
|
||||
ilen = 0
|
||||
output = output .. '\n'
|
||||
for match in input:sub(2):gmatch(utilities.char.utf_8) do
|
||||
for match in input:sub(2):gmatch(utf8) do
|
||||
if ilen < 19 then
|
||||
local spacing = ''
|
||||
for _ = 1, inc do
|
||||
|
@ -109,7 +109,21 @@ local slaps = {
|
||||
function slap:action(msg)
|
||||
local input = utilities.input(msg.text)
|
||||
local victor_id = msg.from.id
|
||||
local victim_id = utilities.id_from_message(self, msg)
|
||||
local victim_id
|
||||
if msg.reply_to_message then
|
||||
victim_id = msg.reply_to_message.from.id
|
||||
else
|
||||
if input then
|
||||
if tonumber(input) then
|
||||
victim_id = tonumber(input)
|
||||
elseif input:match('^@') then
|
||||
local t = utilities.resolve_username(self, input)
|
||||
if t then
|
||||
victim_id = t.id
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
-- IDs
|
||||
if victim_id then
|
||||
if victim_id == victor_id then
|
||||
|
78
otouto/plugins/starwars-crawl.lua
Normal file
78
otouto/plugins/starwars-crawl.lua
Normal file
@ -0,0 +1,78 @@
|
||||
-- Based on a plugin by matthewhesketh.
|
||||
|
||||
local HTTP = require('socket.http')
|
||||
local JSON = require('dkjson')
|
||||
local bindings = require('otouto.bindings')
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
local starwars = {}
|
||||
|
||||
function starwars:init(config)
|
||||
starwars.triggers = utilities.triggers(self.info.username, config.cmd_pat)
|
||||
:t('starwars', true):t('sw', true).table
|
||||
starwars.doc = config.cmd_pat .. [[starwars <query>
|
||||
Returns the opening crawl from the specified Star Wars film.
|
||||
Alias: ]] .. config.cmd_pat .. 'sw'
|
||||
starwars.command = 'starwars <query>'
|
||||
starwars.base_url = 'http://swapi.co/api/films/'
|
||||
end
|
||||
|
||||
local films_by_number = {
|
||||
['phantom menace'] = 4,
|
||||
['attack of the clones'] = 5,
|
||||
['revenge of the sith'] = 6,
|
||||
['new hope'] = 1,
|
||||
['empire strikes back'] = 2,
|
||||
['return of the jedi'] = 3,
|
||||
['force awakens'] = 7
|
||||
}
|
||||
|
||||
local corrected_numbers = {
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
7
|
||||
}
|
||||
|
||||
function starwars:action(msg, config)
|
||||
local input = utilities.input_from_msg(msg)
|
||||
if not input then
|
||||
utilities.send_reply(self, msg, starwars.doc, true)
|
||||
return
|
||||
end
|
||||
|
||||
bindings.sendChatAction(self, { chat_id = msg.chat.id, action = 'typing' } )
|
||||
|
||||
local film
|
||||
if tonumber(input) then
|
||||
input = tonumber(input)
|
||||
film = corrected_numbers[input] or input
|
||||
else
|
||||
for title, number in pairs(films_by_number) do
|
||||
if string.match(input, title) then
|
||||
film = number
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not film then
|
||||
utilities.send_reply(self, msg, config.errors.results)
|
||||
return
|
||||
end
|
||||
|
||||
local url = starwars.base_url .. film
|
||||
local jstr, code = HTTP.request(url)
|
||||
if code ~= 200 then
|
||||
utilities.send_reply(self, msg, config.errors.connection)
|
||||
return
|
||||
end
|
||||
|
||||
local output = '*' .. JSON.decode(jstr).opening_crawl .. '*'
|
||||
utilities.send_message(self, msg.chat.id, output, true, nil, true)
|
||||
end
|
||||
|
||||
return starwars
|
@ -5,6 +5,7 @@ local JSON = require('dkjson')
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
time.command = 'time <location>'
|
||||
time.base_url = 'https://maps.googleapis.com/maps/api/timezone/json?location=%s,%s×tamp=%s'
|
||||
|
||||
function time:init(config)
|
||||
time.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('time', true).table
|
||||
@ -13,15 +14,10 @@ Returns the time, date, and timezone for the given location.]]
|
||||
end
|
||||
|
||||
function time:action(msg, config)
|
||||
|
||||
local input = utilities.input(msg.text)
|
||||
local input = utilities.input_from_msg(msg)
|
||||
if not input then
|
||||
if msg.reply_to_message and msg.reply_to_message.text then
|
||||
input = msg.reply_to_message.text
|
||||
else
|
||||
utilities.send_message(self, msg.chat.id, time.doc, true, msg.message_id, true)
|
||||
return
|
||||
end
|
||||
utilities.send_reply(self, msg, time.doc, true)
|
||||
return
|
||||
end
|
||||
|
||||
local coords = utilities.get_coords(input, config)
|
||||
@ -31,30 +27,33 @@ function time:action(msg, config)
|
||||
end
|
||||
|
||||
local now = os.time()
|
||||
local utc = os.time(os.date("!*t", now))
|
||||
|
||||
local url = 'https://maps.googleapis.com/maps/api/timezone/json?location=' .. coords.lat ..','.. coords.lon .. '×tamp='..utc
|
||||
|
||||
local jstr, res = HTTPS.request(url)
|
||||
if res ~= 200 then
|
||||
local utc = os.time(os.date('!*t', now))
|
||||
local url = time.base_url:format(coords.lat, coords.lon, utc)
|
||||
local jstr, code = HTTPS.request(url)
|
||||
if code ~= 200 then
|
||||
utilities.send_reply(self, msg, config.errors.connection)
|
||||
return
|
||||
end
|
||||
|
||||
local jdat = JSON.decode(jstr)
|
||||
local data = JSON.decode(jstr)
|
||||
if data.status == 'ZERO_RESULTS' then
|
||||
utilities.send_reply(self, msg, config.errors.results)
|
||||
return
|
||||
end
|
||||
|
||||
local timestamp = now + jdat.rawOffset + jdat.dstOffset
|
||||
local utcoff = (jdat.rawOffset + jdat.dstOffset) / 3600
|
||||
local timestamp = now + data.rawOffset + data.dstOffset
|
||||
local utcoff = (data.rawOffset + data.dstOffset) / 3600
|
||||
if utcoff == math.abs(utcoff) then
|
||||
utcoff = '+'.. utilities.pretty_float(utcoff)
|
||||
utcoff = '+' .. utilities.pretty_float(utcoff)
|
||||
else
|
||||
utcoff = utilities.pretty_float(utcoff)
|
||||
end
|
||||
local output = os.date('!%I:%M %p\n', timestamp) .. os.date('!%A, %B %d, %Y\n', timestamp) .. jdat.timeZoneName .. ' (UTC' .. utcoff .. ')'
|
||||
output = '```\n' .. output .. '\n```'
|
||||
|
||||
local output = string.format('```\n%s\n%s (UTC%s)\n```',
|
||||
os.date('!%I:%M %p\n%A, %B %d, %Y', timestamp),
|
||||
data.timeZoneName,
|
||||
utcoff
|
||||
)
|
||||
utilities.send_reply(self, msg, output, true)
|
||||
|
||||
end
|
||||
|
||||
return time
|
||||
|
@ -8,42 +8,38 @@ local utilities = require('otouto.utilities')
|
||||
translate.command = 'translate [text]'
|
||||
|
||||
function translate:init(config)
|
||||
translate.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('translate', true):t('tl', true).table
|
||||
assert(config.yandex_key,
|
||||
'translate.lua requires a Yandex translate API key from http://tech.yandex.com/keys/get.'
|
||||
)
|
||||
|
||||
translate.triggers = utilities.triggers(self.info.username, config.cmd_pat)
|
||||
:t('translate', true):t('tl', true).table
|
||||
translate.doc = config.cmd_pat .. [[translate [text]
|
||||
Translates input or the replied-to message into the bot's language.]]
|
||||
translate.base_url = 'https://translate.yandex.net/api/v1.5/tr.json/translate?key=' .. config.yandex_key .. '&lang=' .. config.lang .. '&text=%s'
|
||||
end
|
||||
|
||||
function translate:action(msg, config)
|
||||
|
||||
local input = utilities.input(msg.text)
|
||||
local input = utilities.input_from_msg(msg)
|
||||
if not input then
|
||||
if msg.reply_to_message and msg.reply_to_message.text then
|
||||
input = msg.reply_to_message.text
|
||||
else
|
||||
utilities.send_message(self, msg.chat.id, translate.doc, true, msg.message_id, true)
|
||||
return
|
||||
end
|
||||
utilities.send_reply(self, msg, translate.doc, true)
|
||||
return
|
||||
end
|
||||
|
||||
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
|
||||
local url = translate.base_url:format(URL.escape(input))
|
||||
local jstr, code = HTTPS.request(url)
|
||||
if code ~= 200 then
|
||||
utilities.send_reply(self, msg, config.errors.connection)
|
||||
return
|
||||
end
|
||||
|
||||
local jdat = JSON.decode(str)
|
||||
if jdat.code ~= 200 then
|
||||
local data = JSON.decode(jstr)
|
||||
if data.code ~= 200 then
|
||||
utilities.send_reply(self, msg, config.errors.connection)
|
||||
return
|
||||
end
|
||||
|
||||
local output = jdat.text[1]
|
||||
output = '*Translation:*\n"' .. utilities.md_escape(output) .. '"'
|
||||
|
||||
utilities.send_reply(self, msg.reply_to_message or msg, output, true)
|
||||
|
||||
utilities.send_reply(self, msg.reply_to_message or msg, utilities.style.enquote('Translation', data.text[1]), true)
|
||||
end
|
||||
|
||||
return translate
|
||||
|
@ -6,50 +6,45 @@ local JSON = require('dkjson')
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
urbandictionary.command = 'urbandictionary <query>'
|
||||
urbandictionary.base_url = 'http://api.urbandictionary.com/v0/define?term='
|
||||
|
||||
function urbandictionary:init(config)
|
||||
urbandictionary.triggers = utilities.triggers(self.info.username, config.cmd_pat)
|
||||
:t('urbandictionary', true):t('ud', true):t('urban', true).table
|
||||
urbandictionary.doc = config.cmd_pat .. [[urbandictionary <query>
|
||||
urbandictionary.doc = [[
|
||||
/urbandictionary <query>
|
||||
Returns a definition from Urban Dictionary.
|
||||
Aliases: ]] .. config.cmd_pat .. 'ud, ' .. config.cmd_pat .. 'urban'
|
||||
Aliases: /ud, /urban
|
||||
]]
|
||||
urbandictionary.doc = urbandictionary.doc:gsub('/', config.cmd_pat)
|
||||
end
|
||||
|
||||
function urbandictionary:action(msg, config)
|
||||
|
||||
local input = utilities.input(msg.text)
|
||||
local input = utilities.input_from_msg(msg)
|
||||
if not input then
|
||||
if msg.reply_to_message and msg.reply_to_message.text then
|
||||
input = msg.reply_to_message.text
|
||||
else
|
||||
utilities.send_message(self, msg.chat.id, urbandictionary.doc, true, msg.message_id, true)
|
||||
return
|
||||
end
|
||||
utilities.send_reply(self, msg, urbandictionary.doc, true)
|
||||
return
|
||||
end
|
||||
|
||||
local url = 'http://api.urbandictionary.com/v0/define?term=' .. URL.escape(input)
|
||||
|
||||
local jstr, res = HTTP.request(url)
|
||||
if res ~= 200 then
|
||||
local url = urbandictionary.base_url .. URL.escape(input)
|
||||
local jstr, code = HTTP.request(url)
|
||||
if code ~= 200 then
|
||||
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, config.errors.results)
|
||||
return
|
||||
local data = JSON.decode(jstr)
|
||||
local output
|
||||
if data.result_type == 'no_results' then
|
||||
output = config.errors.results
|
||||
else
|
||||
output = string.format('*%s*\n\n%s\n\n_%s_',
|
||||
data.list[1].word:gsub('*', '*\\**'),
|
||||
utilities.trim(utilities.md_escape(data.list[1].definition)),
|
||||
utilities.trim((data.list[1].example or '')):gsub('_', '_\\__')
|
||||
)
|
||||
end
|
||||
|
||||
local output = '*' .. jdat.list[1].word .. '*\n\n' .. utilities.trim(jdat.list[1].definition)
|
||||
if string.len(jdat.list[1].example) > 0 then
|
||||
output = output .. '_\n\n' .. utilities.trim(jdat.list[1].example) .. '_'
|
||||
end
|
||||
|
||||
output = output:gsub('%[', ''):gsub('%]', '')
|
||||
|
||||
utilities.send_message(self, msg.chat.id, output, true, nil, true)
|
||||
|
||||
utilities.send_reply(self, msg, output, true)
|
||||
end
|
||||
|
||||
return urbandictionary
|
||||
|
@ -6,11 +6,9 @@ local JSON = require('dkjson')
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
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
|
||||
end
|
||||
assert(config.owm_api_key,
|
||||
'weather.lua requires an OpenWeatherMap API key from http://openweathermap.org/API.'
|
||||
)
|
||||
|
||||
weather.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('weather', true).table
|
||||
weather.doc = config.cmd_pat .. [[weather <location>
|
||||
@ -21,14 +19,10 @@ weather.command = 'weather <location>'
|
||||
|
||||
function weather:action(msg, config)
|
||||
|
||||
local input = utilities.input(msg.text)
|
||||
local input = utilities.input_from_msg(msg)
|
||||
if not input then
|
||||
if msg.reply_to_message and msg.reply_to_message.text then
|
||||
input = msg.reply_to_message.text
|
||||
else
|
||||
utilities.send_message(self, msg.chat.id, weather.doc, true, msg.message_id, true)
|
||||
return
|
||||
end
|
||||
utilities.send_reply(self, msg, weather.doc, true)
|
||||
return
|
||||
end
|
||||
|
||||
local coords = utilities.get_coords(input, config)
|
||||
|
@ -1,51 +1,59 @@
|
||||
local whoami = {}
|
||||
|
||||
local utilities = require('otouto.utilities')
|
||||
local bindings = require('otouto.bindings')
|
||||
|
||||
whoami.command = 'whoami'
|
||||
|
||||
function whoami:init(config)
|
||||
whoami.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('who', true):t('whoami').table
|
||||
whoami.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('who'):t('whoami').table
|
||||
whoami.doc = [[
|
||||
Returns user and chat info for you or the replied-to message.
|
||||
Alias: ]] .. config.cmd_pat .. 'who'
|
||||
end
|
||||
|
||||
function whoami:action(msg)
|
||||
|
||||
if msg.reply_to_message then
|
||||
msg = msg.reply_to_message
|
||||
end
|
||||
|
||||
local from_name = utilities.build_name(msg.from.first_name, msg.from.last_name)
|
||||
|
||||
local chat_id = math.abs(msg.chat.id)
|
||||
if chat_id > 1000000000000 then
|
||||
chat_id = chat_id - 1000000000000
|
||||
end
|
||||
|
||||
local user = 'You are @%s, also known as *%s* `[%s]`'
|
||||
if msg.from.username then
|
||||
user = user:format(utilities.markdown_escape(msg.from.username), from_name, msg.from.id)
|
||||
else
|
||||
user = 'You are *%s* `[%s]`,'
|
||||
user = user:format(from_name, msg.from.id)
|
||||
end
|
||||
|
||||
local group = '@%s, also known as *%s* `[%s]`.'
|
||||
if msg.chat.type == 'private' then
|
||||
group = group:format(utilities.markdown_escape(self.info.username), self.info.first_name, self.info.id)
|
||||
elseif msg.chat.username then
|
||||
group = group:format(utilities.markdown_escape(msg.chat.username), msg.chat.title, chat_id)
|
||||
else
|
||||
group = '*%s* `[%s]`.'
|
||||
group = group:format(msg.chat.title, chat_id)
|
||||
end
|
||||
|
||||
local output = user .. ', and you are messaging ' .. group
|
||||
|
||||
utilities.send_message(self, msg.chat.id, output, true, msg.message_id, true)
|
||||
|
||||
-- Operate on the replied-to message, if it exists.
|
||||
msg = msg.reply_to_message or msg
|
||||
-- If it's a private conversation, bot is chat, unless bot is from.
|
||||
local chat = msg.from.id == msg.chat.id and self.info or msg.chat
|
||||
-- Names for the user and group, respectively. HTML-escaped.
|
||||
local from_name = utilities.html_escape(
|
||||
utilities.build_name(
|
||||
msg.from.first_name,
|
||||
msg.from.last_name
|
||||
)
|
||||
)
|
||||
local chat_name = utilities.html_escape(
|
||||
chat.title
|
||||
or utilities.build_name(chat.first_name, chat.last_name)
|
||||
)
|
||||
-- "Normalize" a group ID so it's not arbitrarily modified by the bot API.
|
||||
local chat_id = math.abs(chat.id)
|
||||
if chat_id > 1000000000000 then chat_id = chat_id - 1000000000000 end
|
||||
-- Do the thing.
|
||||
local output = string.format(
|
||||
'You are %s <code>[%s]</code>, and you are messaging %s <code>[%s]</code>.',
|
||||
msg.from.username and string.format(
|
||||
'@%s, also known as <b>%s</b>',
|
||||
msg.from.username,
|
||||
from_name
|
||||
) or '<b>' .. from_name .. '</b>',
|
||||
msg.from.id,
|
||||
msg.chat.username and string.format(
|
||||
'@%s, also known as <b>%s</b>',
|
||||
chat.username,
|
||||
chat_name
|
||||
) or '<b>' .. chat_name .. '</b>',
|
||||
chat_id
|
||||
)
|
||||
bindings.sendMessage(self, {
|
||||
chat_id = msg.chat.id,
|
||||
reply_to_message_id = msg.message_id,
|
||||
disable_web_page_preview = true,
|
||||
parse_mode = 'HTML',
|
||||
text = output
|
||||
})
|
||||
end
|
||||
|
||||
return whoami
|
||||
|
@ -12,101 +12,79 @@ function wikipedia:init(config)
|
||||
wikipedia.doc = config.cmd_pat .. [[wikipedia <query>
|
||||
Returns an article from Wikipedia.
|
||||
Aliases: ]] .. config.cmd_pat .. 'w, ' .. config.cmd_pat .. 'wiki'
|
||||
end
|
||||
|
||||
local get_title = function(search)
|
||||
for _,v in ipairs(search) do
|
||||
if not v.snippet:match('may refer to:') then
|
||||
return v.title
|
||||
end
|
||||
end
|
||||
return false
|
||||
wikipedia.search_url = 'https://' .. config.lang .. '.wikipedia.org/w/api.php?action=query&list=search&format=json&srsearch='
|
||||
wikipedia.res_url = 'https://' .. config.lang .. '.wikipedia.org/w/api.php?action=query&prop=extracts&format=json&exchars=4000&exsectionformat=plain&titles='
|
||||
wikipedia.art_url = 'https://' .. config.lang .. '.wikipedia.org/wiki/'
|
||||
end
|
||||
|
||||
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.
|
||||
local input = utilities.input(msg.text)
|
||||
local input = utilities.input_from_msg(msg)
|
||||
if not input then
|
||||
if msg.reply_to_message and msg.reply_to_message.text then
|
||||
input = msg.reply_to_message.text
|
||||
else
|
||||
utilities.send_message(self, msg.chat.id, wikipedia.doc, true, msg.message_id, true)
|
||||
return
|
||||
end
|
||||
utilities.send_reply(self, msg, wikipedia.doc, true)
|
||||
return
|
||||
end
|
||||
|
||||
-- This kinda sucks, but whatever.
|
||||
input = input:gsub('#', ' sharp')
|
||||
|
||||
-- Disclaimer: These variables will be reused.
|
||||
local jstr, res, jdat
|
||||
|
||||
-- All pretty standard from here.
|
||||
local search_url = 'https://en.wikipedia.org/w/api.php?action=query&list=search&format=json&srsearch='
|
||||
|
||||
jstr, res = HTTPS.request(search_url .. URL.escape(input))
|
||||
if res ~= 200 then
|
||||
local jstr, code = HTTPS.request(wikipedia.search_url .. URL.escape(input))
|
||||
if code ~= 200 then
|
||||
utilities.send_reply(self, msg, config.errors.connection)
|
||||
return
|
||||
end
|
||||
|
||||
jdat = JSON.decode(jstr)
|
||||
if jdat.query.searchinfo.totalhits == 0 then
|
||||
local data = JSON.decode(jstr)
|
||||
if data.query.searchinfo.totalhits == 0 then
|
||||
utilities.send_reply(self, msg, config.errors.results)
|
||||
return
|
||||
end
|
||||
|
||||
local title = get_title(jdat.query.search)
|
||||
local title
|
||||
for _, v in ipairs(data.query.search) do
|
||||
if not v.snippet:match('may refer to:') then
|
||||
title = v.title
|
||||
break
|
||||
end
|
||||
end
|
||||
if not title then
|
||||
utilities.send_reply(self, msg, config.errors.results)
|
||||
return
|
||||
end
|
||||
|
||||
local res_url = 'https://en.wikipedia.org/w/api.php?action=query&prop=extracts&format=json&exchars=4000&exsectionformat=plain&titles='
|
||||
|
||||
jstr, res = HTTPS.request(res_url .. URL.escape(title))
|
||||
if res ~= 200 then
|
||||
local res_jstr, res_code = HTTPS.request(wikipedia.res_url .. URL.escape(title))
|
||||
if res_code ~= 200 then
|
||||
utilities.send_reply(self, msg, config.errors.connection)
|
||||
return
|
||||
end
|
||||
|
||||
local _
|
||||
local text = JSON.decode(jstr).query.pages
|
||||
_, text = next(text)
|
||||
local _, text = next(JSON.decode(res_jstr).query.pages)
|
||||
if not text then
|
||||
utilities.send_reply(self, msg, config.errors.results)
|
||||
return
|
||||
else
|
||||
text = text.extract
|
||||
end
|
||||
|
||||
-- Remove needless bits from the article, take only the first paragraph.
|
||||
text = text:gsub('</?.->', '')
|
||||
text = text.extract
|
||||
-- Remove crap and take only the first paragraph.
|
||||
text = text:gsub('</?.->', ''):gsub('%[.+%]', '')
|
||||
local l = text:find('\n')
|
||||
if l then
|
||||
text = text:sub(1, l-1)
|
||||
end
|
||||
|
||||
-- This block can be annoying to read.
|
||||
-- We use the initial title to make the url for later use. Then we remove
|
||||
-- the extra bits that won't be in the article. We determine whether the
|
||||
-- first part of the text is the title, and if so, we embolden that.
|
||||
-- Otherwise, we prepend the text with a bold title. Then we append a "Read
|
||||
-- More" link.
|
||||
local url = 'https://en.wikipedia.org/wiki/' .. URL.escape(title)
|
||||
title = title:gsub('%(.+%)', '')
|
||||
local output
|
||||
if string.match(text:sub(1, title:len()), title) then
|
||||
output = '*' .. title .. '*' .. text:sub(title:len()+1)
|
||||
local url = wikipedia.art_url .. URL.escape(title)
|
||||
title = utilities.html_escape(title)
|
||||
-- If the beginning of the article is the title, embolden that.
|
||||
-- Otherwise, we'll add a title in bold.
|
||||
local short_title = title:gsub('%(.+%)', '')
|
||||
local combined_text, count = text:gsub('^'..short_title, '<b>'..short_title..'</b>')
|
||||
local body
|
||||
if count == 1 then
|
||||
body = combined_text
|
||||
else
|
||||
output = '*' .. title:gsub('%(.+%)', '') .. '*\n' .. text:gsub('%[.+%]','')
|
||||
body = '<b>' .. title .. '</b>\n' .. text
|
||||
end
|
||||
output = output .. '\n[Read more.](' .. url:gsub('%)', '\\)') .. ')'
|
||||
|
||||
utilities.send_message(self, msg.chat.id, output, true, nil, true)
|
||||
|
||||
local output = string.format(
|
||||
'%s\n<a href="%s">Read more.</a>',
|
||||
body,
|
||||
utilities.html_escape(url)
|
||||
)
|
||||
utilities.send_message(self, msg.chat.id, output, true, nil, 'html')
|
||||
end
|
||||
|
||||
return wikipedia
|
||||
|
@ -5,52 +5,48 @@ local JSON = require('dkjson')
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
xkcd.command = 'xkcd [i]'
|
||||
xkcd.base_url = 'https://xkcd.com/info.0.json'
|
||||
xkcd.strip_url = 'http://xkcd.com/%s/info.0.json'
|
||||
|
||||
function xkcd:init(config)
|
||||
xkcd.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('xkcd', true).table
|
||||
xkcd.doc = config.cmd_pat .. [[xkcd [i]
|
||||
Returns the latest xkcd strip and its alt text. If a number is given, returns that number strip. If "r" is passed in place of a number, returns a random strip.]]
|
||||
local jstr = HTTP.request(xkcd.base_url)
|
||||
if jstr then
|
||||
local data = JSON.decode(jstr)
|
||||
if data then
|
||||
xkcd.latest = data.num
|
||||
end
|
||||
end
|
||||
xkcd.latest = xkcd.latest or 1700
|
||||
end
|
||||
|
||||
function xkcd:action(msg, config)
|
||||
|
||||
local jstr, res = HTTP.request('http://xkcd.com/info.0.json')
|
||||
if res ~= 200 then
|
||||
local input = utilities.get_word(msg.text, 2)
|
||||
if input == 'r' then
|
||||
input = math.random(xkcd.latest)
|
||||
elseif tonumber(input) then
|
||||
input = tonumber(input)
|
||||
else
|
||||
input = xkcd.latest
|
||||
end
|
||||
local url = xkcd.strip_url:format(input)
|
||||
local jstr, code = HTTP.request(url)
|
||||
if code == 404 then
|
||||
utilities.send_reply(self, msg, config.errors.results)
|
||||
elseif code ~= 200 then
|
||||
utilities.send_reply(self, msg, config.errors.connection)
|
||||
return
|
||||
else
|
||||
local data = JSON.decode(jstr)
|
||||
local output = string.format('*%s (*[%s](%s)*)*\n_%s_',
|
||||
data.safe_title:gsub('*', '*\\**'),
|
||||
data.num,
|
||||
data.img,
|
||||
data.alt:gsub('_', '_\\__')
|
||||
)
|
||||
utilities.send_message(self, msg.chat.id, output, false, nil, true)
|
||||
end
|
||||
local latest = JSON.decode(jstr).num
|
||||
local strip_num = latest
|
||||
|
||||
local input = utilities.input(msg.text)
|
||||
if input then
|
||||
if input == '404' then
|
||||
utilities.send_message(self, msg.chat.id, '*404*\nNot found.', false, nil, true)
|
||||
return
|
||||
elseif tonumber(input) then
|
||||
if tonumber(input) > latest then
|
||||
strip_num = latest
|
||||
else
|
||||
strip_num = input
|
||||
end
|
||||
elseif input == 'r' then
|
||||
strip_num = math.random(latest)
|
||||
end
|
||||
end
|
||||
|
||||
local res_url = 'http://xkcd.com/' .. strip_num .. '/info.0.json'
|
||||
|
||||
jstr, res = HTTP.request(res_url)
|
||||
if res ~= 200 then
|
||||
utilities.send_reply(self, msg, config.errors.connection)
|
||||
return
|
||||
end
|
||||
local jdat = JSON.decode(jstr)
|
||||
|
||||
local output = '*' .. jdat.safe_title .. ' (*[' .. jdat.num .. '](' .. jdat.img .. ')*)*\n_' .. jdat.alt:gsub('_', '\\_') .. '_'
|
||||
|
||||
utilities.send_message(self, msg.chat.id, output, false, nil, true)
|
||||
|
||||
end
|
||||
|
||||
return xkcd
|
||||
|
@ -8,11 +8,9 @@ local JSON = require('dkjson')
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
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
|
||||
end
|
||||
assert(config.google_api_key,
|
||||
'youtube.lua requires a Google API key from http://console.developers.google.com.'
|
||||
)
|
||||
|
||||
youtube.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('youtube', true):t('yt', true).table
|
||||
youtube.doc = config.cmd_pat .. [[youtube <query>
|
||||
@ -24,14 +22,10 @@ youtube.command = 'youtube <query>'
|
||||
|
||||
function youtube:action(msg, config)
|
||||
|
||||
local input = utilities.input(msg.text)
|
||||
local input = utilities.input_from_msg(msg)
|
||||
if not input then
|
||||
if msg.reply_to_message and msg.reply_to_message.text then
|
||||
input = msg.reply_to_message.text
|
||||
else
|
||||
utilities.send_message(self, msg.chat.id, youtube.doc, true, msg.message_id, true)
|
||||
return
|
||||
end
|
||||
utilities.send_reply(self, msg, youtube.doc, true)
|
||||
return
|
||||
end
|
||||
|
||||
local url = 'https://www.googleapis.com/youtube/v3/search?key=' .. config.google_api_key .. '&type=video&part=snippet&maxResults=4&q=' .. URL.escape(input)
|
||||
|
Reference in New Issue
Block a user