diff --git a/bindings.lua b/bindings.lua index fcf4d7a..b6d038e 100755 --- a/bindings.lua +++ b/bindings.lua @@ -7,7 +7,7 @@ assert(JSON) local BASE_URL = 'https://api.telegram.org/bot' .. config.bot_api_key -if not config.bot_api_key then +if config.bot_api_key == '' then error('You did not set your bot token in config.lua!') end @@ -88,6 +88,9 @@ end sendLocation = function(chat_id, latitude, longitude, reply_to_message_id, disable_notification) + if latitude == 0 then latitude = 0.001 end + if longitude == 0 then longitude = 0.001 end + local url = BASE_URL .. '/sendLocation?chat_id=' .. chat_id .. '&latitude=' .. latitude .. '&longitude=' .. longitude if reply_to_message_id then diff --git a/bot.lua b/bot.lua index 8598168..a276d25 100755 --- a/bot.lua +++ b/bot.lua @@ -3,7 +3,7 @@ HTTPS = require('ssl.https') URL = require('socket.url') JSON = require('cjson') -version = '3.4' +version = '3.5' bot_init = function() -- The function run when the bot is started or reloaded. @@ -33,22 +33,39 @@ bot_init = function() -- The function run when the bot is started or reloaded. math.random() last_update = last_update or 0 -- Set loop variables: Update offset, - last_cron = last_cron or os.date('%M', os.time()) -- the time of the last cron job, + last_cron = last_cron or os.date('%M') -- the time of the last cron job, is_started = true -- and whether or not the bot should be running. - database.usernames = database.usernames or {} -- Table to cache usernames by user ID. database.users = database.users or {} -- Table to cache userdata. + database.users[tostring(bot.id)] = bot + + -- Migration code. Remove in 3.6. + if database.lastfm then + for k,v in pairs(database.lastfm) do + if not database.users[k] then database.users[k] = {} end + database.users[k].lastfm = v + end + end + + -- Migration code. Remove in 3.6. + if database.nicknames then + for k,v in pairs(database.nicknames) do + if not database.users[k] then database.users[k] = {} end + database.users[k].nickname = v + end + end end on_msg_receive = function(msg) -- The fn run whenever a message is received. - if msg.from.username then - database.usernames[msg.from.username:lower()] = msg.from.id - end - + -- Create a user entry if it does not exist. if not database.users[tostring(msg.from.id)] then database.users[tostring(msg.from.id)] = {} end + -- Clear things that no longer exist. + database.users[tostring(msg.from.id)].username = nil + database.users[tostring(msg.from.id)].last_name = nil + -- Wee. for k,v in pairs(msg.from) do database.users[tostring(msg.from.id)][k] = v end @@ -77,7 +94,7 @@ on_msg_receive = function(msg) -- The fn run whenever a message is received. end) if not success then sendReply(msg, 'Sorry, an unexpected error occurred.') - handle_exception(result, msg.text) + handle_exception(result, msg.from.id .. ': ' .. msg.text) return end -- If the action returns a table, make that table msg. @@ -107,8 +124,8 @@ while is_started do -- Start a loop while the bot should be running. print(config.errors.connection) end - if last_cron ~= os.date('%M', os.time()) then -- Run cron jobs every minute. - last_cron = os.date('%M', os.time()) + if last_cron ~= os.date('%M') then -- Run cron jobs every minute. + last_cron = os.date('%M') save_data(bot.username..'.db', database) -- Save the database. for i,v in ipairs(plugins) do if v.cron then -- Call each plugin's cron function, if it has one. diff --git a/config.lua b/config.lua index a0546b8..697c4f9 100755 --- a/config.lua +++ b/config.lua @@ -15,7 +15,6 @@ return { -- If you change this, make sure you also modify launch-tg.sh. cli_port = 4567, admin = 00000000, - admin_name = 'John Smith', log_chat = nil, about_text = [[ I am otouto, the plugin-wielding, multi-purpose Telegram bot. diff --git a/plugins/administration.lua b/plugins/administration.lua index 19de901..2a08b85 100644 --- a/plugins/administration.lua +++ b/plugins/administration.lua @@ -1,6 +1,6 @@ --[[ administration.lua - Version 1.4 + Version 1.5 Part of the otouto project. © 2016 topkecleon GNU General Public License, version 2 @@ -13,45 +13,42 @@ Important notices about updates will be here! - The global banlist has been merged with the blacklist. This merge will occur - automatically on versions 1.1 and 1.2. - - Group rules will now be stored in tables rather than pre-numbered strings. - Automatic migration will be in effect in versions 1.2 and 1.3. + Rules lists always exist, empty if there are no rules. Group arrays are now + stored in a "groups" array rather than at the top level. Global data is now + stored at the top level rather than in a "global" array. Automatic migration + will occur in versions 1.5 and 1.6. ]]-- -- Build the administration db if nonexistent. if not database.administration then database.administration = { - global = { - admins = {} - } + admins = {}, + groups = {} } end - -- Create the blacklist db if nonexistant. -database.blacklist = database.blacklist or {} - - -- Migration code: Remove this in v1.3. - -- Global ban list has been merged with blacklist. -if database.administration.global.bans then - for k in pairs(database.administration.global.bans) do - database.blacklist[k] = true - end - database.administration.global.bans = nil -end - - -- Migration code: Remove this in v1.4. - -- Rule lists have been converted from strings to tables. -for k,v in pairs(database.administration) do - if type(v.rules) == 'string' then - local t = {} - for l in v.rules:gmatch('(.-)\n') do - table.insert(t, l:sub(6)) + -- Migration code: Remove this in v1.7. + -- Group data is now stored in a "groups" array. +if not database.administration.groups then + database.administration.groups = {} + for k,v in pairs(database.administration) do + if tonumber(k) then + database.administration.groups[k] = v + database.administration[k] = nil end - v.rules = t end end + -- Global data is stored at the top level. +if database.administration.global then + for k,v in pairs(database.administration.global) do + database.administration[k] = v + end + database.administration.global = nil +end + -- Rule lists remain empty, rather than nil, when there are no rules. +for k,v in pairs(database.administration.groups) do + v.rules = v.rules or {} +end local sender = dofile('lua-tg/sender.lua') tg = sender('localhost', config.cli_port) @@ -82,7 +79,7 @@ local flags = { }, [4] = { name = 'antibot', - desc = 'Prevents the addition of bots by non-moderators. Only useful in non-supergroups.', + desc = 'Prevents the addition of bots by non-moderators.', short = 'This group does not allow users to add bots.', enabled = 'Non-moderators will no longer be able to add bots.', disabled = 'Non-moderators will now be able to add bots.' @@ -109,16 +106,16 @@ local get_rank = function(target, chat) return 5 end - if database.administration.global.admins[target] then + if database.administration.admins[target] then return 4 end - if chat and database.administration[chat] then - if database.administration[chat].govs[target] then + if chat and database.administration.groups[chat] then + if database.administration.groups[chat].govs[target] then return 3 - elseif database.administration[chat].mods[target] then + elseif database.administration.groups[chat].mods[target] then return 2 - elseif database.administration[chat].bans[target] then + elseif database.administration.groups[chat].bans[target] then return 0 end end @@ -133,59 +130,40 @@ end local get_target = function(msg) - local target = {} - if msg.reply_to_message then - local user = msg.reply_to_message.from - if msg.reply_to_message.new_chat_participant then - user = msg.reply_to_message.new_chat_participant - elseif msg.reply_to_message.left_chat_participant then - user = msg.reply_to_message.left_chat_participant - end - target.id = user.id - target.name = user.first_name - if user.last_name then - target.name = user.first_name .. ' ' .. user.last_name - end - else - target.name = 'User' - local input = get_word(msg.text, 2) - if not input then - target.err = 'Please provide a username or ID.' - else - target.id = resolve_username(input) - if target.id == nil then - target.err = 'Sorry, I do not recognize that username.' - elseif target.id == false then - target.err = 'Invalid ID or username.' - end - end - end - + local target = user_from_message(msg) if target.id then - target.id_str = tostring(target.id) target.rank = get_rank(target.id, msg.chat.id) end - return target end -local kick_user = function(target, chat) +local kick_user = function(chat, target) - target = tonumber(target) - chat = tostring(chat) + chat = math.abs(chat) - if database.administration[chat].grouptype == 'group' then - tg:chat_del_user(tonumber(chat), target) + if chat > 1000000000000 then + chat = chat - 1000000000000 + tg:_send('channel_kick channel#' .. chat .. ' user#' .. target .. '\n') else - tg:channel_kick(chat, target) + tg:_send('chat_del_user chat#' .. chat .. ' user#' .. target .. '\n') end end local get_photo = function(chat) - local filename = tg:load_chat_photo(chat) + chat = math.abs(chat) + + local filename + + if chat > 1000000000000 then + chat = chat - 1000000000000 + filename = tg:_send('load_channel_photo channel#' .. chat .. '\n', true) + else + filename = tg:_send('load_chat_photo chat#' .. chat .. '\n', true) + end + if filename:find('FAIL') then print('Error downloading photo for group ' .. chat .. '.') return @@ -195,9 +173,21 @@ local get_photo = function(chat) end +local get_link = function(chat) + + chat = math.abs(chat) + if chat > 1000000000000 then + chat = chat - 1000000000000 + return tg:_send('export_channel_link channel#' .. chat .. '\n', true) + else + return tg:_send('export_chat_link chat#' .. chat .. '\n', true) + end + +end + local get_desc = function(chat_id) - local group = database.administration[tostring(chat_id)] + local group = database.administration.groups[tostring(chat_id)] local output if group.link then output = '*Welcome to* [' .. group.name .. '](' .. group.link .. ')*!*' @@ -207,7 +197,7 @@ local get_desc = function(chat_id) if group.motd then output = output .. '\n\n*Message of the Day:*\n' .. group.motd end - if group.rules then + if #group.rules > 0 then output = output .. '\n\n*Rules:*' for i,v in ipairs(group.rules) do output = output .. '\n*' .. i .. '.* ' .. v @@ -237,15 +227,16 @@ local commands = { privilege = 0, interior = true, - action = function(msg) + action = function(msg, group) if get_rank(msg.from.id, msg.chat.id) > 1 then return true end - if not database.administration[msg.chat.id_str].flags[2] == true then + if not group.flags[2] then return true end - kick_user(msg.from.id, msg.chat.id) - sendMessage(msg.from.id, flags[2].kicked:gsub('GROUPNAME', msg.chat.title)) + kick_user(msg.chat.id, msg.from.id) + local output = flags[2].kicked:gsub('GROUPNAME', msg.chat.title) + sendMessage(msg.from.id, output) end }, @@ -257,14 +248,13 @@ local commands = { privilege = 0, interior = true, - action = function(msg) + action = function(msg, group) local rank = get_rank(msg.from.id, msg.chat.id) - local group = database.administration[msg.chat.id_str] -- banned if rank == 0 then - kick_user(msg.from.id, msg.chat.id) + kick_user(msg.chat.id, msg.from.id) sendMessage(msg.from.id, 'Sorry, you are banned from ' .. msg.chat.title .. '.') return end @@ -274,8 +264,9 @@ local commands = { -- antisquig Strict if group.flags[3] == true then if msg.from.name:match('[\216-\219][\128-\191]') or msg.from.name:match('‮') or msg.from.name:match('‏') then - kick_user(msg.from.id, msg.chat.id) - sendMessage(msg.from.id, flags[3].kicked:gsub('GROUPNAME', msg.chat.title)) + kick_user(msg.chat.id, msg.from.id) + local output = flags[3].kicked:gsub('GROUPNAME', msg.chat.title) + sendMessage(msg.from.id, output) return end end @@ -291,7 +282,7 @@ local commands = { -- banned if get_rank(msg.new_chat_participant.id, msg.chat.id) == 0 then - kick_user(msg.new_chat_participant.id, msg.chat.id) + kick_user(msg.chat.id, msg.new_chat_participant.id) sendMessage(msg.new_chat_participant.id, 'Sorry, you are banned from ' .. msg.chat.title .. '.') return end @@ -299,8 +290,9 @@ local commands = { -- antisquig Strict if group.flags[3] == true then if msg.new_chat_participant.name:match('[\216-\219][\128-\191]') or msg.new_chat_participant.name:match('‮') or msg.new_chat_participant.name:match('‏') then - kick_user(msg.new_chat_participant.id, msg.chat.id) - sendMessage(msg.new_chat_participant.id, flags[3].kicked:gsub('GROUPNAME', msg.chat.title)) + kick_user(msg.chat.id, msg.new_chat_participant.id) + local output = flags[3].kicked:gsub('GROUPNAME', msg.chat.title) + sendMessage(msg.new_chat_participant.id, output) return end end @@ -308,7 +300,7 @@ local commands = { -- antibot if msg.new_chat_participant.username and msg.new_chat_participant.username:match('bot$') then if rank < 2 and group.flags[4] == true then - kick_user(msg.new_chat_participant.id, msg.chat.id) + kick_user(msg.chat.id, msg.new_chat_participant.id) return end else @@ -334,6 +326,8 @@ local commands = { else group.photo = get_photo(msg.chat.id) end + else + group.photo = get_photo(msg.chat.id) end return @@ -345,6 +339,8 @@ local commands = { else group.photo = nil end + else + group.photo = nil end return @@ -367,9 +363,9 @@ local commands = { action = function(msg) local output = '' - for k,v in pairs(database.administration) do - -- no "global" or unlisted groups - if tonumber(k) and not v.flags[1] then + for k,v in pairs(database.administration.groups) do + -- no unlisted groups + if not v.flags[1] then if v.link then output = output .. '• [' .. v.name .. '](' .. v.link .. ')\n' else @@ -399,7 +395,7 @@ local commands = { local rank = get_rank(msg.from.id, msg.chat.id) local output = '*Commands for ' .. ranks[rank] .. ':*\n' for i = 1, rank do - for ind, val in ipairs(database.administration.global.help[i]) do + for ind, val in ipairs(database.administration.help[i]) do output = output .. '• /' .. val .. '\n' end end @@ -422,24 +418,33 @@ local commands = { privilege = 1, interior = true, - action = function(msg) + action = function(msg, group) + local mod_format = function(id) + id = tostring(id) + local user = database.users[id] or { first_name = 'Unknown' } + local name = user.first_name + if user.last_name then name = user.first_name .. ' ' .. user.last_name end + name = markdown_escape(name) + local output = '• ' .. name .. ' `[' .. id .. ']`\n' + return output + end local modstring = '' - for k,v in pairs(database.administration[msg.chat.id_str].mods) do - modstring = modstring .. '• ' .. v .. ' (' .. k .. ')\n' + for k,v in pairs(group.mods) do + modstring = modstring .. mod_format(k) end if modstring ~= '' then modstring = '*Moderators for* _' .. msg.chat.title .. '_ *:*\n' .. modstring end local govstring = '' - for k,v in pairs(database.administration[msg.chat.id_str].govs) do - govstring = govstring .. '• ' .. v .. ' (' .. k .. ')\n' + for k,v in pairs(group.govs) do + govstring = govstring .. mod_format(k) end if govstring ~= '' then govstring = '*Governors for* _' .. msg.chat.title .. '_ *:*\n' .. govstring end - local adminstring = '*Administrators:*\n• ' .. config.admin_name .. ' (' .. config.admin .. ')\n' - for k,v in pairs(database.administration.global.admins) do - adminstring = adminstring .. '• ' .. v .. ' (' .. k .. ')\n' + local adminstring = '*Administrators:*\n' .. mod_format(config.admin) + for k,v in pairs(database.administration.admins) do + adminstring = adminstring .. mod_format(k) end local output = modstring .. govstring .. adminstring sendMessage(msg.chat.id, output, true, nil, true) @@ -476,11 +481,11 @@ local commands = { privilege = 1, interior = true, - action = function(msg) + action = function(msg, group) local output = 'No rules have been set for ' .. msg.chat.title .. '.' - if database.administration[msg.chat.id_str].rules then + if #group.rules > 0 then output = '*Rules for* _' .. msg.chat.title .. '_ *:*\n' - for i,v in ipairs(database.administration[msg.chat.id_str].rules) do + for i,v in ipairs(group.rules) do output = output .. '*' .. i .. '.* ' .. v .. '\n' end end @@ -498,10 +503,10 @@ local commands = { privilege = 1, interior = true, - action = function(msg) + action = function(msg, group) local output = 'No MOTD has been set for ' .. msg.chat.title .. '.' - if database.administration[msg.chat.id_str].motd then - output = '*MOTD for* _' .. msg.chat.title .. '_ *:*\n' .. database.administration[msg.chat.id_str].motd + if group.motd then + output = '*MOTD for* _' .. msg.chat.title .. '_ *:*\n' .. group.motd end sendMessage(msg.chat.id, output, true, nil, true) end @@ -516,10 +521,10 @@ local commands = { privilege = 1, interior = true, - action = function(msg) + action = function(msg, group) local output = 'No link has been set for ' .. msg.chat.title .. '.' - if database.administration[msg.chat.id_str].link then - output = '[' .. msg.chat.title .. '](' .. database.administration[msg.chat.id_str].link .. ')' + if group.link then + output = '[' .. msg.chat.title .. '](' .. group.link .. ')' end sendMessage(msg.chat.id, output, true, nil, true) end @@ -543,7 +548,7 @@ local commands = { local output = 'Leave this group manually or you will be unable to rejoin.' sendMessage(msg.chat.id, output, true, nil, true) else - kick_user(msg.from.id, msg.chat.id) + kick_user(msg.chat.id, msg.from.id) end end }, @@ -566,21 +571,22 @@ local commands = { sendReply(msg, target.name .. ' is too privileged to be kicked.') return end - kick_user(target.id, msg.chat.id) + kick_user(msg.chat.id, target.id) sendMessage(msg.chat.id, target.name .. ' has been kicked.') end }, { -- ban triggers = { - '^/ban[@'..bot.username..']*' + '^/ban', + '^/unban' }, command = 'ban ', privilege = 2, interior = true, - action = function(msg) + action = function(msg, group) local target = get_target(msg) if target.err then sendReply(msg, target.err) @@ -590,41 +596,14 @@ local commands = { sendReply(msg, target.name .. ' is too privileged to be banned.') return end - if database.administration[msg.chat.id_str].bans[target.id_str] then - sendReply(msg, target.name .. ' is already banned.') - return + if group.bans[target.id_str] then + group.bans[target.id_str] = nil + sendReply(msg, target.name .. ' has been unbanned.') + else + group.bans[target.id_str] = true + kick_user(msg.chat.id, target.id) + sendReply(msg, target.name .. ' has been banned.') end - kick_user(target.id, msg.chat.id) - database.administration[msg.chat.id_str].bans[target.id_str] = true - sendMessage(msg.chat.id, target.name .. ' has been banned.') - end - }, - - { -- unban - triggers = { - '^/unban[@'..bot.username..']*' - }, - - command = 'unban ', - privilege = 2, - interior = true, - - action = function(msg) - local target = get_target(msg) - if target.err then - sendReply(msg, target.err) - return - end - if not database.administration[msg.chat.id_str].bans[target.id_str] then - if database.blacklist[target.id_str] then - sendReply(msg, target.name .. ' is globally banned.') - else - sendReply(msg, target.name .. ' is not banned.') - end - return - end - database.administration[msg.chat.id_str].bans[target.id_str] = nil - sendMessage(msg.chat.id, target.name .. ' has been unbanned.') end }, @@ -638,7 +617,7 @@ local commands = { privilege = 3, interior = true, - action = function(msg) + action = function(msg, group) local usage = 'usage: `/changerule `\n`/changerule -- `deletes.' local input = msg.text:input() if not input then @@ -658,24 +637,24 @@ local commands = { sendMessage(msg.chat.id, output, true, msg.message_id, true) return end - if not database.administration[msg.chat.id_str].rules then + if not group.rules then local output = 'Sorry, there are no rules to change. Please use /setrules.\n' .. usage sendMessage(msg.chat.id, output, true, msg.message_id, true) return end - if not database.administration[msg.chat.id_str].rules[rule_num] then - rule_num = #database.administration[msg.chat.id_str].rules + 1 + if not group.rules[rule_num] then + rule_num = #group.rules + 1 end if rule_new == '--' or rule_new == '—' then - if database.administration[msg.chat.id_str].rules[rule_num] then - table.remove(database.administration[msg.chat.id_str].rules, rule_num) + if group.rules[rule_num] then + table.remove(group.rules, rule_num) sendReply(msg, 'That rule has been deleted.') else sendReply(msg, 'There is no rule with that number.') end return end - database.administration[msg.chat.id_str].rules[rule_num] = rule_new + group.rules[rule_num] = rule_new local output = '*' .. rule_num .. '*. ' .. rule_new sendMessage(msg.chat.id, output, true, nil, true) end @@ -690,20 +669,24 @@ local commands = { privilege = 3, interior = true, - action = function(msg) - local input = msg.text:match('^/setrules[@'..bot.username..']*(.+)') + action = function(msg, group) + local input = msg.text:match('^/setrules[@'..bot.username..']* (.+)') if not input then sendReply(msg, '/setrules [rule]\n\n[rule]\n...') return + elseif input == '--' or input == '—' then + group.rules = {} + sendReply(msg, 'The rules have been cleared.') + return end - database.administration[msg.chat.id_str].rules = {} + group.rules = {} input = input:trim() .. '\n' local output = '*Rules for* _' .. msg.chat.title .. '_ *:*\n' local i = 1 for l in input:gmatch('(.-)\n') do output = output .. '*' .. i .. '.* ' .. l .. '\n' i = i + 1 - table.insert(database.administration[msg.chat.id_str].rules, l:trim()) + table.insert(group.rules, l:trim()) end sendMessage(msg.chat.id, output, true, nil, true) end @@ -718,14 +701,18 @@ local commands = { privilege = 3, interior = true, - action = function(msg) + action = function(msg, group) local input = msg.text:input() if not input then - sendReply(msg, '/' .. command) + sendReply(msg, 'Please specify the new message of the day.') + return + elseif input == '--' or input == '—' then + group.motd = nil + sendReply(msg, 'The MOTD has been cleared.') return end input = input:trim() - database.administration[msg.chat.id_str].motd = input + group.motd = input local output = '*MOTD for* _' .. msg.chat.title .. '_ *:*\n' .. input sendMessage(msg.chat.id, output, true, nil, true) end @@ -740,13 +727,17 @@ local commands = { privilege = 3, interior = true, - action = function(msg) + action = function(msg, group) local input = msg.text:input() if not input then - sendReply(msg, '/' .. command) + sendReply(msg, 'Please specify the new link.') + return + elseif input == '--' or input == '—' then + group.link = get_link(msg.chat.id) + sendReply(msg, 'The link has been regenerated.') return end - database.administration[msg.chat.id_str].link = input + group.link = input local output = '[' .. msg.chat.title .. '](' .. input .. ')' sendMessage(msg.chat.id, output, true, nil, true) end @@ -761,7 +752,7 @@ local commands = { privilege = 3, interior = true, - action = function(msg) + action = function(msg, group) local input = msg.text:input() if input then input = get_word(input, 1) @@ -771,18 +762,18 @@ local commands = { if not input then local output = '*Flags for* _' .. msg.chat.title .. '_ *:*\n' for i,v in ipairs(flags) do - local status = database.administration[msg.chat.id_str].flags[i] or false + local status = group.flags[i] or false output = output .. '`[' .. i .. ']` *' .. v.name .. '*` = ' .. tostring(status) .. '`\n• ' .. v.desc .. '\n' end sendMessage(msg.chat.id, output, true, nil, true) return end local output - if database.administration[msg.chat.id_str].flags[input] == true then - database.administration[msg.chat.id_str].flags[input] = false + if group.flags[input] == true then + group.flags[input] = false sendReply(msg, flags[input].disabled) else - database.administration[msg.chat.id_str].flags[input] = true + group.flags[input] = true sendReply(msg, flags[input].enabled) end end @@ -790,119 +781,83 @@ local commands = { { -- mod triggers = { - '^/mod[@'..bot.username..']*$' + '^/mod', + '^/demod' }, command = 'mod ', privilege = 3, interior = true, - action = function(msg) - if not msg.reply_to_message then - sendReply(msg, 'This command must be run via reply.') - return - end - local target = get_target(msg) - if target.rank > 1 then - sendReply(msg, target.name .. ' is already a moderator or greater.') - return - end - if database.administration[msg.chat.id_str].grouptype == 'supergroup' then - tg:channel_set_admin(msg.chat.id, target, 1) - end - database.administration[msg.chat.id_str].mods[target.id_str] = target.name - sendReply(msg, target.name .. ' is now a moderator.') - end - }, - - { -- demod - triggers = { - '^/demod[@'..bot.username..']*' - }, - - command = 'demod ', - privilege = 3, - interior = true, - - action = function(msg) + action = function(msg, group) local target = get_target(msg) if target.err then sendReply(msg, target.err) return end - if target.rank ~= 2 then - sendReply(msg, target.name .. ' is not a moderator.') - return + if group.mods[target.id_str] then + if group.grouptype == 'supergroup' then + tg:channel_set_admin(msg.chat.id, target, 0) + end + group.mods[target.id_str] = nil + sendReply(msg, target.name .. ' is no longer a moderator.') + else + if target.rank > 2 then + sendReply(msg, target.name .. ' is greater than a moderator.') + return + end + if group.grouptype == 'supergroup' then + tg:channel_set_admin(msg.chat.id, target, 1) + end + group.mods[target.id_str] = true + sendReply(msg, target.name .. ' is now a moderator.') end - if database.administration[msg.chat.id_str].grouptype == 'supergroup' then - tg:channel_set_admin(msg.chat.id, target, 0) - end - database.administration[msg.chat.id_str].mods[target.id_str] = nil - sendReply(msg, target.name .. ' is no longer a moderator.') end - }, { -- gov triggers = { - '^/gov[@'..bot.username..']*$' + '^/gov', + '^/degov' }, command = 'gov ', privilege = 4, interior = true, - action = function(msg) - if not msg.reply_to_message then - sendReply(msg, 'This command must be run via reply.') - return - end - local target = get_target(msg) - if target.rank > 2 then - sendReply(msg, target.name .. ' is already a governor or greater.') - return - elseif target.rank == 2 then - database.administration[msg.chat.id_str].mods[target.id_str] = nil - end - if database.administration[msg.chat.id_str].grouptype == 'supergroup' then - tg:channel_set_admin(msg.chat.id, target, 1) - end - database.administration[msg.chat.id_str].govs[target.id_str] = target.name - sendReply(msg, target.name .. ' is now a governor.') - end - }, - - { -- degov - triggers = { - '^/degov[@'..bot.username..']*' - }, - - command = 'degov ', - privilege = 4, - interior = true, - - action = function(msg) + action = function(msg, group) local target = get_target(msg) if target.err then sendReply(msg, target.err) return end - if target.rank ~= 3 then - sendReply(msg, target.name .. ' is not a governor.') - return + if group.govs[target.id_str] then + if group.grouptype == 'supergroup' then + tg:channel_set_admin(msg.chat.id, target, 0) + end + group.govs[target.id_str] = nil + sendReply(msg, target.name .. ' is no longer a governor.') + else + if target.rank > 3 then + sendReply(msg, target.name .. ' is greater than a governor.') + return + end + if target.rank == 2 then + group.mods[target.id_str] = nil + end + if group.grouptype == 'supergroup' then + tg:channel_set_admin(msg.chat.id, target, 1) + end + group.govs[target.id_str] = true + sendReply(msg, target.name .. ' is now a governor.') end - if database.administration[msg.chat.id_str].grouptype == 'supergroup' then - tg:channel_set_admin(msg.chat.id, target, 0) - end - database.administration[msg.chat.id_str].govs[target.id_str] = nil - sendReply(msg, target.name .. ' is no longer a governor.') end }, { -- hammer triggers = { - '^/hammer[@'..bot.username..']*', - '^/banall[@'..bot.username..']*' + '^/hammer', + '^/unhammer' }, command = 'hammer ', @@ -920,27 +875,26 @@ local commands = { return end if database.blacklist[target.id_str] then - sendReply(msg, target.name .. ' is already globally banned.') - return - end - for k,v in pairs(database.administration) do - if tonumber(k) then - kick_user(target.id, k) + database.blacklist[target.id_str] = nil + sendReply(msg, target.name .. ' has been globally unbanned.') + else + database.blacklist[target.id_str] = true + for k,v in pairs(database.administration.groups) do + kick_user(k, target.id) end + sendReply(msg, target.name .. ' has been globally banned.') end - database.blacklist[target.id_str] = true - sendReply(msg, target.name .. ' has been globally banned.') end }, - { -- unhammer + { -- admin triggers = { - '^/unhammer[@'..bot.username..']*', - '^/unbanall[@'..bot.username..']*' + '^/admin', + '^/deadmin' }, - command = 'unhammer ', - privilege = 4, + command = 'admin 3 then - sendReply(msg, target.name .. ' is already an administrator or greater.') - return - elseif target.rank == 2 then - database.administration[msg.chat.id_str].mods[target.id_str] = nil - elseif target.rank == 3 then - database.administration[msg.chat.id_str].govs[target.id_str] = nil - end - database.administration.global.admins[target.id_str] = target.name - sendReply(msg, target.name .. ' is now an administrator.') - end - }, - - { -- deadmin - triggers = { - '^/deadmin[@'..bot.username..']*' - }, - - command = 'deadmin ', - privilege = 5, - interior = false, - - action = function(msg) - local target = get_target(msg) - if target.rank ~= 4 then - sendReply(msg, target.name .. ' is not an administrator.') - return - end - database.administration.global.admins[target.id_str] = nil - sendReply(msg, target.name .. ' is no longer an administrator.') end }, @@ -1016,23 +931,22 @@ local commands = { interior = false, action = function(msg) - if database.administration[msg.chat.id_str] then + if database.administration.groups[msg.chat.id_str] then sendReply(msg, 'I am already administrating this group.') return end - database.administration[msg.chat.id_str] = { + database.administration.groups[msg.chat.id_str] = { mods = {}, govs = {}, bans = {}, flags = {}, + rules = {}, grouptype = msg.chat.type, name = msg.chat.title, + link = get_link(msg.chat.id), + photo = get_photo(msg.chat.id), founded = os.time() } - if msg.chat.type == 'group' then - database.administration[msg.chat.id_str].photo = get_photo(msg.chat.id) - database.administration[msg.chat.id_str].link = tg:export_chat_link(msg.chat.id) - end sendReply(msg, 'I am now administrating this group.') end }, @@ -1050,15 +964,15 @@ local commands = { action = function(msg) local input = msg.text:input() if input then - if database.administration[input] then - database.administration[input] = nil + if database.administration.groups[input] then + database.administration.groups[input] = nil sendReply(msg, 'I am no longer administrating that group.') else sendReply(msg, 'I do not administrate that group.') end else - if database.administration[msg.chat.id_str] then - database.administration[msg.chat.id_str] = nil + if database.administration.groups[msg.chat.id_str] then + database.administration.groups[msg.chat.id_str] = nil sendReply(msg, 'I am no longer administrating this group.') else sendReply(msg, 'I do not administrate this group.') @@ -1083,7 +997,7 @@ local commands = { return end input = '*Admin Broadcast:*\n' .. input - for k,v in pairs(database.administration) do + for k,v in pairs(database.administration.groups) do if tonumber(k) then sendMessage(k, input, true, nil, true) end @@ -1101,28 +1015,27 @@ for i,v in ipairs(commands) do end end -database.administration.global.help = {} +database.administration.help = {} for i,v in ipairs(ranks) do - database.administration.global.help[i] = {} + database.administration.help[i] = {} end for i,v in ipairs(commands) do if v.command then - table.insert(database.administration.global.help[v.privilege], v.command) + table.insert(database.administration.help[v.privilege], v.command) end end - local action = function(msg) for i,v in ipairs(commands) do for key,val in pairs(v.triggers) do if msg.text_lower:match(val) then - if v.interior and not database.administration[msg.chat.id_str] then + if v.interior and not database.administration.groups[msg.chat.id_str] then break end if msg.chat.type ~= 'private' and get_rank(msg.from.id, msg.chat.id) < v.privilege then break end - local res = v.action(msg) + local res = v.action(msg, database.administration.groups[msg.chat.id_str]) if res ~= true then return res end diff --git a/plugins/blacklist.lua b/plugins/blacklist.lua index f6286af..df14993 100755 --- a/plugins/blacklist.lua +++ b/plugins/blacklist.lua @@ -11,51 +11,22 @@ local triggers = { local action = function(msg) - if database.blacklist[msg.from.id_str] then - return -- End if the sender is blacklisted. + if database.blacklist[msg.from.id_str] then return end + if not msg.text:match('^/blacklist') then return true end + if msg.from.id ~= config.admin then return end + + local target = user_from_message(msg) + if target.err then + sendReply(msg, target.err) + return end - if not string.match(msg.text_lower, '^/blacklist') then - return true - end - - if msg.from.id ~= config.admin then - return -- End if the user isn't admin. - end - - local target, input - if msg.reply_to_message then - target = msg.reply_to_message.from.id + if database.blacklist[tostring(target.id)] then + database.blacklist[tostring(target.id)] = nil + sendReply(msg, target.name .. ' has been removed from the blacklist.') else - input = msg.text:input() - if input then - input = get_word(input, 1) - if tonumber(input) then - target = input - else - target = resolve_username(input) - if target == nil then - sendReply(msg, 'Sorry, I do not recognize that username.') - return - elseif target == false then - sendReply(msg, 'Invalid ID or username.') - return - end - end - else - sendReply(msg, 'You must use this command via reply or by specifying an ID or username.') - return - end - end - - target = tostring(target) - - if database.blacklist[target] then - database.blacklist[target] = nil - sendReply(msg, input .. ' has been removed from the blacklist.') - else - database.blacklist[target] = true - sendReply(msg, input .. ' has been added to the blacklist.') + database.blacklist[tostring(target.id)] = true + sendReply(msg, target.name .. ' has been added to the blacklist.') end end diff --git a/plugins/greetings.lua b/plugins/greetings.lua index 1d18309..8964701 100755 --- a/plugins/greetings.lua +++ b/plugins/greetings.lua @@ -1,12 +1,12 @@ -- Put this on the bottom of your plugin list, after help.lua. local triggers = { - bot.first_name .. '%p?$' + bot.first_name .. '%p*$' } local action = function(msg) - local nick = database.nicknames[msg.from.id_str] or msg.from.first_name + local nick = database.users[msg.from.id_str].nickname or msg.from.first_name for k,v in pairs(config.greetings) do for key,val in pairs(v) do diff --git a/plugins/hackernews.lua b/plugins/hackernews.lua index 03afea6..1747bc8 100755 --- a/plugins/hackernews.lua +++ b/plugins/hackernews.lua @@ -40,6 +40,10 @@ local action = function(msg) title = title:sub(1, 45) .. '...' end local url = res_jdat.url + if not url then + sendReply(msg, config.errors.connection) + return + end if url:find('%(') then output = output .. '• ' .. title .. '\n' .. url:gsub('_', '\\_') .. '\n' else diff --git a/plugins/lastfm.lua b/plugins/lastfm.lua index 9e271c7..ee07874 100755 --- a/plugins/lastfm.lua +++ b/plugins/lastfm.lua @@ -4,10 +4,6 @@ if not config.lastfm_api_key then return end -if not database.lastfm then - database.lastfm = {} -end - local HTTP = require('socket.http') HTTP.TIMEOUT = 1 @@ -17,7 +13,7 @@ local doc = [[``` Returns what you are or were last listening to. If you specify a username, info will be returned for that username. /fmset -Sets your last.fm username. Otherwise, /np will use your Telegram username. Use "/fmset -" to delete it. +Sets your last.fm username. Otherwise, /np will use your Telegram username. Use "/fmset --" to delete it. ```]] local triggers = { @@ -36,11 +32,11 @@ local action = function(msg) elseif string.match(msg.text, '^/fmset') then if not input then sendMessage(msg.chat.id, doc, true, msg.message_id, true) - elseif input == '-' then - database.lastfm[msg.from.id_str] = nil + elseif input == '--' or input == '—' then + database.users[msg.from.id_str].lastfm = nil sendReply(msg, 'Your last.fm username has been forgotten.') else - database.lastfm[msg.from.id_str] = input + database.users[msg.from.id_str].lastfm = input sendReply(msg, 'Your last.fm username has been set to "' .. input .. '".') end return @@ -52,12 +48,12 @@ local action = function(msg) local output = '' if input then username = input - elseif database.lastfm[msg.from.id_str] then - username = database.lastfm[msg.from.id_str] + elseif database.users[msg.from.id_str].lastfm then + username = database.users[msg.from.id_str].lastfm elseif msg.from.username then username = msg.from.username output = '\n\nYour username has been set to ' .. username .. '.\nTo change it, use /fmset .' - database.lastfm[msg.from.id_str] = username + database.users[msg.from.id_str].lastfm = username else sendReply(msg, 'Please specify your last.fm username or set it with /fmset.') return diff --git a/plugins/me.lua b/plugins/me.lua new file mode 100644 index 0000000..6e3eac3 --- /dev/null +++ b/plugins/me.lua @@ -0,0 +1,33 @@ +local triggers = { + '^/me', + '^/me@'..bot.username +} + +local action = function(msg) + + local target = database.users[msg.from.id_str] + + if msg.from.id == config.admin and (msg.reply_to_message or msg.text:input()) then + target = user_from_message(msg) + if target.err then + sendReply(msg, target.err) + return + end + end + + local output = '' + for k,v in pairs(target) do + output = output .. '*' .. k .. ':* `' .. v .. '`\n' + end + output = output .. '\n' + for k,v in pairs(msg.chat) do + output = output .. '*' .. k .. ':* `' .. v .. '`\n' + end + sendMessage(msg.chat.id, output, true, nil, true) + +end + +return { + triggers = triggers, + action = action +} diff --git a/plugins/nick.lua b/plugins/nick.lua index 50c1a64..d8b97a1 100755 --- a/plugins/nick.lua +++ b/plugins/nick.lua @@ -1,7 +1,3 @@ -if not database.nicknames then - database.nicknames = {} -end - local command = 'nick ' local doc = [[``` /nick @@ -28,18 +24,18 @@ local action = function(msg) local output local input = msg.text:input() if not input then - if database.nicknames[target.id_str] then - output = target.name .. '\'s nickname is "' .. database.nicknames[target.id_str] .. '".' + if database.users[target.id_str].nickname then + output = target.name .. '\'s nickname is "' .. database.users[target.id_str].nickname .. '".' else output = target.name .. ' currently has no nickname.' end elseif string.len(input) > 32 then output = 'The character limit for nicknames is 32.' elseif input == '--' or input == '—' then - database.nicknames[target.id_str] = nil + database.users[target.id_str].nickname = nil output = target.name .. '\'s nickname has been deleted.' else - database.nicknames[target.id_str] = input + database.users[target.id_str].nickname = input output = target.name .. '\'s nickname has been set to "' .. input .. '".' end diff --git a/plugins/slap.lua b/plugins/slap.lua index 354d8ef..3e9c894 100755 --- a/plugins/slap.lua +++ b/plugins/slap.lua @@ -96,16 +96,16 @@ local action = function(msg) local victim = msg.text:input() if msg.reply_to_message then - if database.nicknames[tostring(msg.reply_to_message.from.id)] then - victim = database.nicknames[tostring(msg.reply_to_message.from.id)] + if database.users[tostring(msg.reply_to_message.from.id)].nickname then + victim = database.users[tostring(msg.reply_to_message.from.id)].nickname else victim = msg.reply_to_message.from.first_name end end local victor = msg.from.first_name - if database.nicknames[msg.from.id_str] then - victor = database.nicknames[msg.from.id_str] + if database.users[msg.from.id_str].nickname then + victor = database.users[msg.from.id_str].nickname end if not victim then @@ -117,6 +117,8 @@ local action = function(msg) message = message:gsub('VICTIM', victim) message = message:gsub('VICTOR', victor) + message = latcyr(message) + sendMessage(msg.chat.id, message) end diff --git a/plugins/whoami.lua b/plugins/whoami.lua index 4bf2d36..3374448 100755 --- a/plugins/whoami.lua +++ b/plugins/whoami.lua @@ -32,10 +32,6 @@ local action = function(msg) local message = 'You are ' .. from_name .. ' and you are messaging ' .. to_name - if database.nicknames[msg.from.id_str] then - message = message .. '\nYour nickname is ' .. database.nicknames[msg.from.id_str] .. '.' - end - sendReply(msg, message) end diff --git a/plugins/youtube.lua b/plugins/youtube.lua index e6a1f69..b1e6b10 100755 --- a/plugins/youtube.lua +++ b/plugins/youtube.lua @@ -45,9 +45,8 @@ local action = function(msg) return end - local i = math.random(jdat.pageInfo.resultsPerPage) - local vid_url = 'https://www.youtube.com/watch?v=' .. jdat.items[i].id.videoId - local vid_title = jdat.items[i].snippet.title + local vid_url = 'https://www.youtube.com/watch?v=' .. jdat.items[1].id.videoId + local vid_title = jdat.items[1].snippet.title vid_title = vid_title:gsub('%(.+%)',''):gsub('%[.+%]','') local output = '[' .. vid_title .. '](' .. vid_url .. ')' diff --git a/utilities.lua b/utilities.lua index 4fbb4b2..1151b8a 100755 --- a/utilities.lua +++ b/utilities.lua @@ -130,22 +130,61 @@ table_size = function(tab) end -resolve_username = function(target) - -- If $target is a known username, returns associated ID. - -- If $target is an unknown username, returns nil. - -- If $target is a number, returns that number. - -- Otherwise, returns false. +resolve_username = function(input) - local input = tostring(target):lower() - if input:match('^@') then - local uname = input:gsub('^@', '') - return database.usernames[uname] - else - return tonumber(target) or false + input = input:gsub('^@', '') + for k,v in pairs(database.users) do + if v.username and v.username:lower() == input:lower() then + return v + end end end +user_from_message = function(msg) + + local input = msg.text_lower:input() + local target = {} + if msg.reply_to_message then + target = msg.reply_to_message.from + elseif input and tonumber(input) then + target.id = input + if database.users[input] then + for k,v in pairs(database.users[input]) do + target[k] = v + end + end + elseif input and input:match('^@') then + local uname = input:gsub('^@', '') + for k,v in pairs(database.users) do + if v.username and uname == v.username:lower() then + for key, val in pairs(v) do + target[key] = val + end + end + end + if not target.id then + target.err = 'Sorry, I don\'t recognize that username.' + end + else + target.err = 'Please specify a user via reply, ID, or username.' + end + + if target.id then + target.id_str = tostring(target.id) + end + + if not target.first_name then target.first_name = 'User' end + + target.name = target.first_name + if target.last_name then + target.name = target.first_name .. ' ' .. target.last_name + end + + return target + +end + handle_exception = function(err, message) if not err then err = '' end @@ -196,3 +235,13 @@ download_file = function(url, filename) return filename end + +markdown_escape = function(text) + + text = text:gsub('_', '\\_') + text = text:gsub('%[', '\\[') + text = text:gsub('%*', '\\*') + text = text:gsub('`', '\\`') + return text + +end