Merge Upstream
This commit is contained in:
		@@ -6,7 +6,7 @@ Der multifunktionale Telegram-Bot.
 | 
			
		||||
[Offizielle Webseite](https://ponywave.de/projekte/mikubot-v2/) | [Entwickler auf Telegram](http://telegram.me/Akamaru) **KEIN SUPPORT!** | [Offizieller Telegram-Kanal](https://telegram.me/Mikubot_Updates)
 | 
			
		||||
 | 
			
		||||
Mikubot ist ein auf Plugins basierender Bot, der die [offizielle Telegram Bot API](http://core.telegram.org/bots/api) benutzt. Geforkt wurde er von [Brawlbot](https://github.com/Brawl345/Brawlbot-v2) Ursprünglich wurde er 2015 auf Basis von Yagops [Telegram Bot](https://github.com/yagop/telegram-bot/) entwickelt, da aber die Entwicklung von tg-cli [zum Stillstand](https://brawlbot.tk/posts/ein-neuanfang) gekommen ist, wurden alle Plugins des bisher proprietären Brawlbots im Juni 2016 auf die Bot-API portiert und open-sourced. Im Juli und August 2016 wurden die zusätzlichen Plugins von Mikubot ebenfalls auf die Bot-API migriert.  
 | 
			
		||||
**Mikubot V2 basiert auf [otouto](https://github.com/topkecleon/otouto) von Topkecleon.**
 | 
			
		||||
**Mikubot V2 basiert auf [otouto](https://github.com/topkecleon/otouto) von topkecleon.**
 | 
			
		||||
 | 
			
		||||
Mikubot V2 ist freie Software; du darfst ihn modifizieren und weiterverbreiten, allerdings musst du dich an die GNU Affero General Public License v3 halten, siehe **LICENSE** für Details.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12
									
								
								miku/bot.lua
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								miku/bot.lua
									
									
									
									
									
								
							@@ -135,7 +135,7 @@ function bot:on_callback_receive(callback, msg, config) -- whenever a new callba
 | 
			
		||||
  
 | 
			
		||||
  -- Check if whitelist is enabled and user/chat is whitelisted
 | 
			
		||||
  local whitelist = redis:get('whitelist:enabled')
 | 
			
		||||
  if whitelist and not is_sudo(msg, config) then
 | 
			
		||||
  if whitelist and not is_sudo(callback, config) then
 | 
			
		||||
	local hash = 'whitelist:user#id'..user_id
 | 
			
		||||
	local allowed = redis:get(hash) or false
 | 
			
		||||
	if not allowed then
 | 
			
		||||
@@ -163,10 +163,12 @@ function bot:on_callback_receive(callback, msg, config) -- whenever a new callba
 | 
			
		||||
  for n=1, #self.plugins do
 | 
			
		||||
    local plugin = self.plugins[n]
 | 
			
		||||
	if plugin.name == called_plugin then
 | 
			
		||||
	  if is_plugin_disabled_on_chat(plugin.name, msg) then return end
 | 
			
		||||
	  if is_plugin_disabled_on_chat(plugin.name, msg) then utilities.answer_callback_query(callback, 'Plugin wurde in diesem Chat deaktiviert.') return end
 | 
			
		||||
	  plugin:callback(callback, msg, self, config, param)
 | 
			
		||||
	end
 | 
			
		||||
  end
 | 
			
		||||
  
 | 
			
		||||
   utilities.answer_callback_query(callback, 'Ungültiger CallbackQuery: Kein Plugin gefunden.')
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- NOTE: To enable InlineQuerys, send /setinline to @BotFather
 | 
			
		||||
@@ -192,6 +194,11 @@ function bot:process_inline_query(inline_query, config) -- When an inline query
 | 
			
		||||
    inline_query.query = inline_query.query:gsub('"', '\\"')
 | 
			
		||||
  end
 | 
			
		||||
  
 | 
			
		||||
  if string.len(inline_query.query) > 200 then
 | 
			
		||||
    abort_inline_query(inline_query)
 | 
			
		||||
	return
 | 
			
		||||
  end
 | 
			
		||||
  
 | 
			
		||||
  for n=1, #self.plugins do
 | 
			
		||||
    local plugin = self.plugins[n]
 | 
			
		||||
    match_inline_plugins(self, inline_query, config, plugin)
 | 
			
		||||
@@ -227,7 +234,6 @@ function bot:run(config)
 | 
			
		||||
		-- Run cron jobs every minute.
 | 
			
		||||
		if self.last_cron ~= os.date('%M') then
 | 
			
		||||
			self.last_cron = os.date('%M')
 | 
			
		||||
			utilities.save_data(self.info.username..'.db', self.database) -- Save the database.
 | 
			
		||||
		    for n=1, #self.plugins do 
 | 
			
		||||
			    local v = self.plugins[n]
 | 
			
		||||
				if v.cron then -- Call each plugin's cron function, if it has one.
 | 
			
		||||
 
 | 
			
		||||
@@ -43,7 +43,7 @@ function app_store:send_appstore_data(data)
 | 
			
		||||
  else
 | 
			
		||||
    game_center = ''
 | 
			
		||||
  end
 | 
			
		||||
  local category_count = tablelength(data.genres)
 | 
			
		||||
  local category_count = #data.genres
 | 
			
		||||
  if category_count == 1 then
 | 
			
		||||
    category = '\nKategorie: '..data.genres[1]
 | 
			
		||||
  else
 | 
			
		||||
 
 | 
			
		||||
@@ -124,10 +124,10 @@ function bitly_create:action(msg, config, matches)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  if matches[2] == nil then
 | 
			
		||||
    long_url = url_encode(matches[1])
 | 
			
		||||
    long_url = URL.encode(matches[1])
 | 
			
		||||
	domain = 'bit.ly'
 | 
			
		||||
  else
 | 
			
		||||
    long_url = url_encode(matches[2])
 | 
			
		||||
    long_url = URL.encode(matches[2])
 | 
			
		||||
	domain = matches[1]
 | 
			
		||||
  end
 | 
			
		||||
  utilities.send_reply(msg, bitly_create:create_bitlink(long_url, domain, bitly_access_token))
 | 
			
		||||
 
 | 
			
		||||
@@ -47,7 +47,7 @@ function channels:pre_process(msg, config)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function channels:action(msg, config, matches)
 | 
			
		||||
  if msg.from.id ~= config.admin then
 | 
			
		||||
  if not is_sudo(msg, config) then
 | 
			
		||||
    utilities.send_reply(msg, config.errors.sudo, true)
 | 
			
		||||
	return
 | 
			
		||||
  end
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,7 @@ end
 | 
			
		||||
 | 
			
		||||
function control:action(msg, config)
 | 
			
		||||
 | 
			
		||||
	if msg.from.id ~= config.admin then
 | 
			
		||||
	if not is_sudo(msg, config) then
 | 
			
		||||
		return
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -89,7 +89,7 @@ end
 | 
			
		||||
 | 
			
		||||
function creds_manager:action(msg, config, matches)
 | 
			
		||||
  local receiver = msg.from.id
 | 
			
		||||
  if receiver ~= config.admin then
 | 
			
		||||
  if not is_sudo(msg, config) then
 | 
			
		||||
    utilities.send_reply(msg, config.errors.sudo, true)
 | 
			
		||||
	return
 | 
			
		||||
  end
 | 
			
		||||
 
 | 
			
		||||
@@ -14,11 +14,11 @@ local BASE_URL = 'https://mobil.dhl.de'
 | 
			
		||||
function dhl:sendungsstatus(id)
 | 
			
		||||
  local url = BASE_URL..'/shipmentdetails.html?shipmentId='..id
 | 
			
		||||
  local res,code = https.request(url)
 | 
			
		||||
  if code ~= 200 then return 'Fehler beim Abrufen von mobil.dhl.de' end
 | 
			
		||||
  local status = string.match(res, '<div id%=\"detailShortStatus\">(.-)</div>')
 | 
			
		||||
  local status = all_trim(status)
 | 
			
		||||
  local zeit = string.match(res, '<div id%=\"detailStatusDateTime\">(.-)</div>')
 | 
			
		||||
  local zeit = all_trim(zeit)
 | 
			
		||||
  if code ~= 200 then return "Fehler beim Abrufen von mobil.dhl.de" end
 | 
			
		||||
  local status = string.match(res, "<div id%=\"detailShortStatus\">(.-)</div>")
 | 
			
		||||
  local status = utilities.trim(status)
 | 
			
		||||
  local zeit = string.match(res, "<div id%=\"detailStatusDateTime\">(.-)</div>")
 | 
			
		||||
  local zeit = utilities.trim(zeit)
 | 
			
		||||
  if not zeit or zeit == '<br />' then
 | 
			
		||||
    return status
 | 
			
		||||
  end
 | 
			
		||||
 
 | 
			
		||||
@@ -26,6 +26,19 @@ end
 | 
			
		||||
 | 
			
		||||
gImages.command = 'img <Suchbegriff>'
 | 
			
		||||
 | 
			
		||||
function gImages:is_blacklisted(msg)
 | 
			
		||||
  _blacklist = redis:smembers("telegram:img_blacklist")
 | 
			
		||||
  local var = false
 | 
			
		||||
  for v,word in pairs(_blacklist) do
 | 
			
		||||
    if string.find(string.lower(msg), string.lower(word)) then
 | 
			
		||||
      print("Wort steht auf der Blacklist!")
 | 
			
		||||
      var = true
 | 
			
		||||
      break
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
  return var
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Yes, the callback is copied from below, but I can't think of another method :\
 | 
			
		||||
function gImages:callback(callback, msg, self, config, input)
 | 
			
		||||
  if not msg then return end
 | 
			
		||||
@@ -153,7 +166,7 @@ function gImages:action(msg, config, matches)
 | 
			
		||||
  end
 | 
			
		||||
  
 | 
			
		||||
  print ('Überprüfe, ob das Wort auf der Blackliste steht: '..input)
 | 
			
		||||
  if is_blacklisted(input) then
 | 
			
		||||
  if gImages:is_blacklisted(input) then
 | 
			
		||||
    utilities.send_reply(msg, 'Vergiss es!')
 | 
			
		||||
	return
 | 
			
		||||
  end
 | 
			
		||||
 
 | 
			
		||||
@@ -26,6 +26,19 @@ end
 | 
			
		||||
 | 
			
		||||
gImages_nsfw.command = 'img2 <Suchbegriff>'
 | 
			
		||||
 | 
			
		||||
function gImages_nsfw:is_blacklisted(msg)
 | 
			
		||||
  _blacklist = redis:smembers("telegram:img_blacklist")
 | 
			
		||||
  local var = false
 | 
			
		||||
  for v,word in pairs(_blacklist) do
 | 
			
		||||
    if string.find(string.lower(msg), string.lower(word)) then
 | 
			
		||||
      print("Wort steht auf der Blacklist!")
 | 
			
		||||
      var = true
 | 
			
		||||
      break
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
  return var
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Yes, the callback is copied from below, but I can't think of another method :\
 | 
			
		||||
function gImages_nsfw:callback(callback, msg, self, config, input)
 | 
			
		||||
  if not msg then return end
 | 
			
		||||
@@ -153,7 +166,7 @@ function gImages_nsfw:action(msg, config, matches)
 | 
			
		||||
  end
 | 
			
		||||
  
 | 
			
		||||
  print ('Überprüfe, ob das Wort auf der Blackliste steht: '..input)
 | 
			
		||||
  if is_blacklisted(input) then
 | 
			
		||||
  if gImages_nsfw:is_blacklisted(input) then
 | 
			
		||||
    utilities.send_reply(msg, 'Vergiss es!')
 | 
			
		||||
	return
 | 
			
		||||
  end
 | 
			
		||||
 
 | 
			
		||||
@@ -75,7 +75,7 @@ function games:send_game_data(game_id, self, msg)
 | 
			
		||||
  
 | 
			
		||||
  if xml.find(result, 'Genres') then
 | 
			
		||||
    local genres = xml.find(result, 'Genres')
 | 
			
		||||
    local genre_count = tablelength(genres)-1
 | 
			
		||||
    local genre_count = #genres-1
 | 
			
		||||
    if genre_count == 1 then
 | 
			
		||||
      genre = '\nGenre: '..genres[1][1]
 | 
			
		||||
    else
 | 
			
		||||
 
 | 
			
		||||
@@ -180,7 +180,7 @@ function gh_feed:action(msg, config, matches)
 | 
			
		||||
  
 | 
			
		||||
  -- For channels
 | 
			
		||||
  if matches[1] == 'sub' and matches[2] and matches[3] then
 | 
			
		||||
    if msg.from.id ~= config.admin then
 | 
			
		||||
    if not is_sudo(msg, config) then
 | 
			
		||||
      utilities.send_reply(msg, config.errors.sudo, true)
 | 
			
		||||
	  return
 | 
			
		||||
    end
 | 
			
		||||
@@ -194,7 +194,7 @@ function gh_feed:action(msg, config, matches)
 | 
			
		||||
	utilities.send_reply(msg, output, true)
 | 
			
		||||
	return
 | 
			
		||||
  elseif matches[1] == 'del' and matches[2] and matches[3] then
 | 
			
		||||
    if msg.from.id ~= config.admin then
 | 
			
		||||
    if not is_sudo(msg, config) then
 | 
			
		||||
      utilities.send_reply(msg, config.errors.sudo, true)
 | 
			
		||||
	  return
 | 
			
		||||
    end
 | 
			
		||||
@@ -227,7 +227,7 @@ function gh_feed:action(msg, config, matches)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  if matches[1] == 'sub' and matches[2] then
 | 
			
		||||
    if msg.from.id ~= config.admin then
 | 
			
		||||
    if not is_sudo(msg, config) then
 | 
			
		||||
      utilities.send_reply(msg, config.errors.sudo, true)
 | 
			
		||||
	  return
 | 
			
		||||
    end
 | 
			
		||||
@@ -235,7 +235,7 @@ function gh_feed:action(msg, config, matches)
 | 
			
		||||
	utilities.send_reply(msg, output, true)
 | 
			
		||||
	return
 | 
			
		||||
  elseif matches[1] == 'del' and matches[2] then
 | 
			
		||||
    if msg.from.id ~= config.admin then
 | 
			
		||||
    if not is_sudo(msg, config) then
 | 
			
		||||
      utilities.send_reply(msg, config.errors.sudo, true)
 | 
			
		||||
	  return
 | 
			
		||||
    end
 | 
			
		||||
@@ -247,7 +247,7 @@ function gh_feed:action(msg, config, matches)
 | 
			
		||||
	utilities.send_reply(msg, list_subs, true, keyboard)
 | 
			
		||||
    return
 | 
			
		||||
  elseif matches[1] == 'sync' then
 | 
			
		||||
    if msg.from.id ~= config.admin then
 | 
			
		||||
    if not is_sudo(msg, config) then
 | 
			
		||||
      utilities.send_reply(msg, config.errors.sudo, true)
 | 
			
		||||
	  return
 | 
			
		||||
    end
 | 
			
		||||
 
 | 
			
		||||
@@ -63,27 +63,27 @@ function id:action(msg, config, matches)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	local chat_id = msg.chat.id
 | 
			
		||||
	local user = 'Du bist @%s, auch bekannt als *%s* `[%s]`'
 | 
			
		||||
	local user = 'Du bist @%s, auch bekannt als <b>%s</b> <code>[%s]</code>'
 | 
			
		||||
	if msg.from.username then
 | 
			
		||||
		user = user:format(utilities.markdown_escape(msg.from.username), msg.from.name, msg.from.id)
 | 
			
		||||
		user = user:format(utilities.html_escape(msg.from.username), msg.from.name, msg.from.id)
 | 
			
		||||
	else
 | 
			
		||||
		user = 'Du bist *%s* `[%s]`,'
 | 
			
		||||
		user = 'Du bist <b>%s</b> <code>[%s]</code>,'
 | 
			
		||||
		user = user:format(msg.from.name, msg.from.id)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	local group = '@%s, auch bekannt als *%s* `[%s]`.'
 | 
			
		||||
	local group = '@%s, auch bekannt als <b>%s</b> <code>[%s]</code>.'
 | 
			
		||||
	if msg.chat.type == 'private' then
 | 
			
		||||
		group = group:format(utilities.markdown_escape(self.info.username), self.info.first_name, self.info.id)
 | 
			
		||||
		group = group:format(utilities.html_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)
 | 
			
		||||
		group = group:format(utilities.html_escape(msg.chat.username), msg.chat.title, chat_id)
 | 
			
		||||
	else
 | 
			
		||||
		group = '*%s* `[%s]`.'
 | 
			
		||||
		group = '<b>%s</b> <code>[%s]</code>.'
 | 
			
		||||
		group = group:format(msg.chat.title, chat_id)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	local output = user .. ', und du bist in der Gruppe ' .. group
 | 
			
		||||
 | 
			
		||||
	utilities.send_message(msg.chat.id, output, true, msg.message_id, true)
 | 
			
		||||
	utilities.send_message(msg.chat.id, output, true, msg.message_id, 'HTML')
 | 
			
		||||
  elseif matches[1] == "chat" then
 | 
			
		||||
    if msg.chat.type ~= 'group' and msg.chat.type ~= 'supergroup' then
 | 
			
		||||
	  utilities.send_reply(msg, 'Das hier ist keine Gruppe!')
 | 
			
		||||
@@ -115,21 +115,21 @@ function id:action(msg, config, matches)
 | 
			
		||||
	local result = id:get_member_count(msg, chat_id)
 | 
			
		||||
	local member_count = result.result
 | 
			
		||||
	if member_count == 1 then
 | 
			
		||||
	  member_count = 'ist *1 Mitglied'
 | 
			
		||||
	  member_count = 'ist <b>1 Mitglied'
 | 
			
		||||
	else
 | 
			
		||||
	  member_count = 'sind *'..member_count..' Mitglieder'
 | 
			
		||||
	  member_count = 'sind <b>'..member_count..' Mitglieder'
 | 
			
		||||
	end
 | 
			
		||||
    local text = 'IDs für *'..chat_name..'* `['..chat_id..']`\nHier '..member_count..':*\n---------\n'
 | 
			
		||||
    local text = 'IDs für <b>'..chat_name..'</b> <code>['..chat_id..']</code>\nHier '..member_count..':</b>\n---------\n'
 | 
			
		||||
    for k,user in pairs(users_info) do
 | 
			
		||||
	  if table.contains(admins, tostring(user.id)) then
 | 
			
		||||
	    text = text..'*'..user.name..'* `['..user.id..']` _Administrator_\n'
 | 
			
		||||
	    text = text..'<b>'..user.name..'</b> <code>['..user.id..']</code> <i>Administrator</i>\n'
 | 
			
		||||
	  elseif tostring(creator_id) == user.id then
 | 
			
		||||
	    text = text..'*'..user.name..'* `['..user.id..']` _Gründer_\n'
 | 
			
		||||
	    text = text..'<b>'..user.name..'</b> <code>['..user.id..']</code> <i>Gründer</i>\n'
 | 
			
		||||
	  else
 | 
			
		||||
        text = text..'*'..user.name..'* `['..user.id..']`\n'
 | 
			
		||||
        text = text..'<b>'..user.name..'</b> <code>['..user.id..']</code>\n'
 | 
			
		||||
	  end
 | 
			
		||||
    end
 | 
			
		||||
	utilities.send_reply(msg, text..'_(Bots sind nicht gelistet)_', true)
 | 
			
		||||
	utilities.send_reply(msg, text..'<i>(Bots sind nicht gelistet)</i>', 'HTML')
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -49,7 +49,7 @@ function imgblacklist:remove_blacklist(word)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function imgblacklist:action(msg, config, matches)
 | 
			
		||||
  if msg.from.id ~= config.admin then
 | 
			
		||||
  if not is_sudo(msg, config) then
 | 
			
		||||
    utilities.send_reply(msg, config.errors.sudo, true)
 | 
			
		||||
	return
 | 
			
		||||
  end
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,7 @@ end
 | 
			
		||||
 | 
			
		||||
function luarun:action(msg, config)
 | 
			
		||||
 | 
			
		||||
    if msg.from.id ~= config.admin then
 | 
			
		||||
	if not is_sudo(msg, config) then
 | 
			
		||||
        return true
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -16,7 +16,7 @@ lyrics.command = 'lyrics <Lied>'
 | 
			
		||||
 | 
			
		||||
function lyrics:getLyrics(text)
 | 
			
		||||
  local apikey = cred_data.lyricsnmusic_apikey
 | 
			
		||||
  local q = url_encode(text)
 | 
			
		||||
  local q = URL.encode(text)
 | 
			
		||||
  local b = http.request("http://api.lyricsnmusic.com/songs?api_key="..apikey.."&q=" .. q)
 | 
			
		||||
  response = json.decode(b)
 | 
			
		||||
  local reply = ""
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,7 @@
 | 
			
		||||
local media = {}
 | 
			
		||||
 | 
			
		||||
local mime = (loadfile "./miku/mimetype.lua")()
 | 
			
		||||
 | 
			
		||||
media.triggers = {
 | 
			
		||||
    	"(https?://[%w-_%.%?%.:,/%+=&%[%]]+%.(gif))$",
 | 
			
		||||
    	"(https?://[%w-_%.%?%.:,/%+=&%[%]]+%.(mp4))$",
 | 
			
		||||
 
 | 
			
		||||
@@ -24,6 +24,18 @@ end
 | 
			
		||||
 | 
			
		||||
plugin_manager.command = 'plugins <nur für Superuser>'
 | 
			
		||||
 | 
			
		||||
-- Returns at table of lua files inside plugins
 | 
			
		||||
function plugin_manager:plugins_names()
 | 
			
		||||
  local files = {}
 | 
			
		||||
  for k, v in pairs(scandir("miku/plugins")) do
 | 
			
		||||
    -- Ends with .lua
 | 
			
		||||
    if (v:match(".lua$")) then
 | 
			
		||||
	  files[#files+1] = v
 | 
			
		||||
    end 
 | 
			
		||||
  end
 | 
			
		||||
  return files
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Returns the key (index) in the config.enabled_plugins table
 | 
			
		||||
function plugin_manager:plugin_enabled(name, chat)
 | 
			
		||||
  for k,v in pairs(enabled_plugins) do
 | 
			
		||||
@@ -37,7 +49,7 @@ end
 | 
			
		||||
 | 
			
		||||
-- Returns true if file exists in plugins folder
 | 
			
		||||
function plugin_manager:plugin_exists(name)
 | 
			
		||||
  for k,v in pairs(plugins_names()) do
 | 
			
		||||
  for k,v in pairs(plugin_manager:plugins_names()) do
 | 
			
		||||
    if name..'.lua' == v then
 | 
			
		||||
      return true
 | 
			
		||||
    end
 | 
			
		||||
@@ -47,7 +59,7 @@ end
 | 
			
		||||
 | 
			
		||||
function plugin_manager:list_plugins()
 | 
			
		||||
  local text = ''
 | 
			
		||||
  for k, v in pairs(plugins_names()) do
 | 
			
		||||
  for k, v in pairs(plugin_manager:plugins_names()) do
 | 
			
		||||
    --  ✔ enabled, ❌ disabled
 | 
			
		||||
    local status = '❌'
 | 
			
		||||
    -- Check if is enabled
 | 
			
		||||
@@ -161,7 +173,7 @@ function plugin_manager:reenable_plugin_on_chat(msg, plugin)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function plugin_manager:action(msg, config, matches)
 | 
			
		||||
  if msg.from.id ~= config.admin then
 | 
			
		||||
  if not is_sudo(msg, config) then
 | 
			
		||||
    utilities.send_reply(msg, config.errors.sudo, true)
 | 
			
		||||
	return
 | 
			
		||||
  end
 | 
			
		||||
 
 | 
			
		||||
@@ -74,6 +74,7 @@ function pocket:add_pocket_item(access_token, url)
 | 
			
		||||
  local code = result.item.response_code
 | 
			
		||||
  
 | 
			
		||||
  local text = title..' ('..given_url..') hinzugefügt!'
 | 
			
		||||
  if not code then return text end
 | 
			
		||||
  if code ~= "200" and code ~= "0" then text = text..'\nAber die Seite liefert Fehler '..code..' zurück.' end
 | 
			
		||||
  return text
 | 
			
		||||
end
 | 
			
		||||
 
 | 
			
		||||
@@ -21,10 +21,6 @@ end
 | 
			
		||||
quotes.command = 'quote'
 | 
			
		||||
 | 
			
		||||
function quotes:save_quote(msg)
 | 
			
		||||
  if msg.text:sub(11):isempty() then
 | 
			
		||||
    return "Benutzung: /addquote [Zitat]"
 | 
			
		||||
  end
 | 
			
		||||
  
 | 
			
		||||
  local quote = msg.text:sub(11)
 | 
			
		||||
  local hash = get_redis_hash(msg, 'quotes')
 | 
			
		||||
  print('Saving quote to redis set '..hash)
 | 
			
		||||
@@ -33,10 +29,6 @@ function quotes:save_quote(msg)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function quotes:delete_quote(msg)
 | 
			
		||||
  if msg.text:sub(11):isempty() then
 | 
			
		||||
    return "Benutzung: /delquote [Zitat]"
 | 
			
		||||
  end
 | 
			
		||||
  
 | 
			
		||||
  local quote = msg.text:sub(11)
 | 
			
		||||
  local hash = get_redis_hash(msg, 'quotes')
 | 
			
		||||
  print('Deleting quote from redis set '..hash)
 | 
			
		||||
 
 | 
			
		||||
@@ -224,7 +224,7 @@ function rss:action(msg, config, matches)
 | 
			
		||||
  
 | 
			
		||||
  -- For channels
 | 
			
		||||
  if matches[1] == 'sub' and matches[2] and matches[3] then
 | 
			
		||||
    if msg.from.id ~= config.admin then
 | 
			
		||||
    if not is_sudo(msg, config) then
 | 
			
		||||
      utilities.send_reply(msg, config.errors.sudo, true)
 | 
			
		||||
	  return
 | 
			
		||||
    end
 | 
			
		||||
@@ -238,7 +238,7 @@ function rss:action(msg, config, matches)
 | 
			
		||||
	utilities.send_reply(msg, output, 'HTML')
 | 
			
		||||
	return
 | 
			
		||||
  elseif matches[1] == 'del' and matches[2] and matches[3] then
 | 
			
		||||
    if msg.from.id ~= config.admin then
 | 
			
		||||
    if not is_sudo(msg, config) then
 | 
			
		||||
      utilities.send_reply(msg, config.errors.sudo, true)
 | 
			
		||||
	  return
 | 
			
		||||
    end
 | 
			
		||||
@@ -271,7 +271,7 @@ function rss:action(msg, config, matches)
 | 
			
		||||
  end
 | 
			
		||||
  
 | 
			
		||||
  if matches[1] == 'sub' and matches[2] then
 | 
			
		||||
    if msg.from.id ~= config.admin then
 | 
			
		||||
    if not is_sudo(msg, config) then
 | 
			
		||||
      utilities.send_reply(msg, config.errors.sudo, true)
 | 
			
		||||
	  return
 | 
			
		||||
    end
 | 
			
		||||
@@ -279,7 +279,7 @@ function rss:action(msg, config, matches)
 | 
			
		||||
	utilities.send_reply(msg, output, 'HTML')
 | 
			
		||||
	return
 | 
			
		||||
  elseif matches[1] == 'del' and matches[2] then
 | 
			
		||||
    if msg.from.id ~= config.admin then
 | 
			
		||||
    if not is_sudo(msg, config) then
 | 
			
		||||
      utilities.send_reply(msg, config.errors.sudo, true)
 | 
			
		||||
	  return
 | 
			
		||||
    end
 | 
			
		||||
@@ -291,7 +291,7 @@ function rss:action(msg, config, matches)
 | 
			
		||||
	utilities.send_reply(msg, list_subs, 'HTML', keyboard)
 | 
			
		||||
    return
 | 
			
		||||
  elseif matches[1] == 'sync' then
 | 
			
		||||
    if msg.from.id ~= config.admin then
 | 
			
		||||
    if not is_sudo(msg, config) then
 | 
			
		||||
      utilities.send_reply(msg, config.errors.sudo, true)
 | 
			
		||||
	  return
 | 
			
		||||
    end
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,7 @@ function site_header:init(config)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function site_header:action(msg, config, matches)
 | 
			
		||||
  if msg.from.id ~= config.admin then
 | 
			
		||||
  if not is_sudo(msg, config) then
 | 
			
		||||
	utilities.send_reply(msg, config.errors.sudo, true)
 | 
			
		||||
  end
 | 
			
		||||
  
 | 
			
		||||
 
 | 
			
		||||
@@ -136,7 +136,7 @@ function stats:action(msg, config, matches)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    if matches[2] == "chat" then
 | 
			
		||||
	  if msg.from.id ~= config.admin then
 | 
			
		||||
	  if not is_sudo(msg, config) then
 | 
			
		||||
        utilities.send_reply(msg, config.errors.sudo, true)
 | 
			
		||||
	    return
 | 
			
		||||
      else
 | 
			
		||||
 
 | 
			
		||||
@@ -16,10 +16,10 @@ end
 | 
			
		||||
 | 
			
		||||
function tagesschau:get_tagesschau_article(article)
 | 
			
		||||
  local url = BASE_URL..'/'..article..'.json'
 | 
			
		||||
  local res,code  = https.request(url)
 | 
			
		||||
  local data = json.decode(res)
 | 
			
		||||
  local res, code  = https.request(url)
 | 
			
		||||
  if code == 404 then return "Artikel nicht gefunden!" end
 | 
			
		||||
  if code ~= 200 then return "HTTP-Fehler" end
 | 
			
		||||
  local data = json.decode(res)
 | 
			
		||||
  if not data then return "HTTP-Fehler" end
 | 
			
		||||
  if data.type ~= "story" then
 | 
			
		||||
    print('Typ "'..data.type..'" wird nicht unterstützt')
 | 
			
		||||
 
 | 
			
		||||
@@ -248,7 +248,7 @@ end
 | 
			
		||||
 | 
			
		||||
function twitter_send:action(msg, config, matches)
 | 
			
		||||
  if matches[1] == "twwhitelist add" then
 | 
			
		||||
    if msg.from.id ~= config.admin then
 | 
			
		||||
    if not is_sudo(msg, config) then
 | 
			
		||||
      utilities.send_reply(msg, config.errors.sudo, true)
 | 
			
		||||
	  return
 | 
			
		||||
    else
 | 
			
		||||
@@ -265,7 +265,7 @@ function twitter_send:action(msg, config, matches)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  if matches[1] == "twwhitelist del" then
 | 
			
		||||
    if msg.from.id ~= config.admin then
 | 
			
		||||
    if not is_sudo(msg, config) then
 | 
			
		||||
      utilities.send_reply(msg, config.errors.sudo, true)
 | 
			
		||||
	  return
 | 
			
		||||
    else
 | 
			
		||||
@@ -288,7 +288,7 @@ function twitter_send:action(msg, config, matches)
 | 
			
		||||
  -- Thanks to the great doc at https://github.com/ignacio/LuaOAuth#a-more-involved-example
 | 
			
		||||
  if not oauth_token and not oauth_token_secret then
 | 
			
		||||
    if msg.chat.type == 'group' or msg.chat.type == 'supergroup' then
 | 
			
		||||
	  if msg.from.id ~= config.admin then
 | 
			
		||||
	  if not is_sudo(msg, config) then
 | 
			
		||||
        utilities.send_reply(msg, config.errors.sudo, true)
 | 
			
		||||
	    return
 | 
			
		||||
      else
 | 
			
		||||
@@ -312,7 +312,7 @@ function twitter_send:action(msg, config, matches)
 | 
			
		||||
 | 
			
		||||
  if matches[1] == 'auth' and matches[2] then
 | 
			
		||||
    if msg.chat.type == 'group' or msg.chat.type == 'supergroup' then
 | 
			
		||||
      if msg.from.id ~= config.admin then
 | 
			
		||||
      if not is_sudo(msg, config) then
 | 
			
		||||
        utilities.send_reply(msg, config.errors.sudo, true)
 | 
			
		||||
	    return
 | 
			
		||||
      end
 | 
			
		||||
@@ -327,7 +327,7 @@ function twitter_send:action(msg, config, matches)
 | 
			
		||||
  
 | 
			
		||||
  if matches[1] == 'unauth' then
 | 
			
		||||
    if msg.chat.type == 'group' or msg.chat.type == 'supergroup' then
 | 
			
		||||
      if msg.from.id ~= config.admin then
 | 
			
		||||
      if not is_sudo(msg, config) then
 | 
			
		||||
        utilities.send_reply(msg, config.errors.sudo, true)
 | 
			
		||||
	    return
 | 
			
		||||
      end
 | 
			
		||||
 
 | 
			
		||||
@@ -28,15 +28,13 @@ socket = require('socket')
 | 
			
		||||
URL = require('socket.url')
 | 
			
		||||
json = require('dkjson')
 | 
			
		||||
pcall(json.use_lpeg)
 | 
			
		||||
serpent = require('serpent')
 | 
			
		||||
redis = (loadfile './miku/redis.lua')()
 | 
			
		||||
mime = (loadfile './miku/mimetype.lua')()
 | 
			
		||||
OAuth = require 'OAuth'
 | 
			
		||||
helpers = require 'OAuth.helpers'
 | 
			
		||||
require('/miku/encoding')
 | 
			
		||||
serpent = require("serpent")
 | 
			
		||||
redis = (loadfile "./miku/redis.lua")()
 | 
			
		||||
OAuth = require "OAuth"
 | 
			
		||||
helpers = require "OAuth.helpers"
 | 
			
		||||
 | 
			
		||||
http.TIMEOUT = 7
 | 
			
		||||
https.TIMEOUT = 7
 | 
			
		||||
http.TIMEOUT = 10
 | 
			
		||||
https.TIMEOUT = 10
 | 
			
		||||
 | 
			
		||||
 -- For the sake of ease to new contributors and familiarity to old contributors,
 | 
			
		||||
 -- we'll provide a couple of aliases to real bindings here.
 | 
			
		||||
@@ -343,38 +341,21 @@ function utilities.input_from_msg(msg)
 | 
			
		||||
	return utilities.input(msg.text) or (msg.reply_to_message and #msg.reply_to_message.text > 0 and msg.reply_to_message.text) or false
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Calculates the length of the given string as UTF-8 characters
 | 
			
		||||
function utilities.utf8_len(s)
 | 
			
		||||
    local chars = 0
 | 
			
		||||
    for i = 1, string.len(s) do
 | 
			
		||||
        local b = string.byte(s, i)
 | 
			
		||||
        if b < 128 or b >= 192 then
 | 
			
		||||
            chars = chars + 1
 | 
			
		||||
        end
 | 
			
		||||
    end
 | 
			
		||||
    return chars
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Trims whitespace from a string.
 | 
			
		||||
function utilities.trim(str)
 | 
			
		||||
	local s = str:gsub('^%s*(.-)%s*$', '%1')
 | 
			
		||||
	return s
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Returns true if the string is empty
 | 
			
		||||
-- Returns true if the string is blank/empty
 | 
			
		||||
function string:isempty()
 | 
			
		||||
  self = utilities.trim(self)
 | 
			
		||||
  return self == nil or self == ''
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Returns true if the string is blank
 | 
			
		||||
function string:isblank()
 | 
			
		||||
  self = self:trim()
 | 
			
		||||
  return self:isempty()
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function get_name(msg)
 | 
			
		||||
   local name = msg.from.first_name
 | 
			
		||||
   if name == nil then
 | 
			
		||||
   if not name then
 | 
			
		||||
      name = msg.from.id
 | 
			
		||||
   end
 | 
			
		||||
   return name
 | 
			
		||||
@@ -392,10 +373,6 @@ function convert_timestamp(timestamp, date_format)
 | 
			
		||||
  return os.date(date_format, timestamp)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function string.starts(String, Start)
 | 
			
		||||
   return Start == string.sub(String,1,string.len(Start))
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Saves file to $HOME/tmp/. If file_name isn't provided,
 | 
			
		||||
-- will get the text after the last "/" for filename
 | 
			
		||||
-- and content-type for extension
 | 
			
		||||
@@ -498,19 +475,6 @@ function utilities.build_name(first, last)
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function utilities:resolve_username(input)
 | 
			
		||||
	input = input:gsub('^@', '')
 | 
			
		||||
	for _, user in pairs(self.database.users) do
 | 
			
		||||
		if user.username and user.username:lower() == input:lower() then
 | 
			
		||||
			local t = {}
 | 
			
		||||
			for key, val in pairs(user) do
 | 
			
		||||
				t[key] = val
 | 
			
		||||
			end
 | 
			
		||||
			return t
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function utilities:handle_exception(err, message, log_chat)
 | 
			
		||||
    local output = string.format(
 | 
			
		||||
        '[%s]\n%s: %s\n%s\n',
 | 
			
		||||
@@ -528,19 +492,12 @@ function utilities:handle_exception(err, message, log_chat)
 | 
			
		||||
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- MOVED TO DOWNLOAD_TO_FILE
 | 
			
		||||
function utilities.download_file(url, filename)
 | 
			
		||||
  return download_to_file(url, filename)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function utilities.md_escape(text)
 | 
			
		||||
	return text:gsub('_', '\\_')
 | 
			
		||||
			:gsub('%[', '\\['):gsub('%]', '\\]')
 | 
			
		||||
			:gsub('%*', '\\*'):gsub('`', '\\`')
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
utilities.markdown_escape = utilities.md_escape
 | 
			
		||||
 | 
			
		||||
function utilities.html_escape(text)
 | 
			
		||||
	return text:gsub('&', '&'):gsub('<', '<'):gsub('>', '>')
 | 
			
		||||
end
 | 
			
		||||
@@ -566,13 +523,6 @@ function utilities.triggers(username, cmd_pat, trigger_table)
 | 
			
		||||
	return self
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function utilities.with_http_timeout(timeout, fun)
 | 
			
		||||
	local original = http.TIMEOUT
 | 
			
		||||
	http.TIMEOUT = timeout
 | 
			
		||||
	fun()
 | 
			
		||||
	http.TIMEOUT = original
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function utilities.enrich_user(user)
 | 
			
		||||
	user.id_str = tostring(user.id)
 | 
			
		||||
	user.name = utilities.build_name(user.first_name, user.last_name)
 | 
			
		||||
@@ -595,11 +545,11 @@ function utilities.enrich_message(msg)
 | 
			
		||||
	if msg.forward_from then
 | 
			
		||||
		msg.forward_from = utilities.enrich_user(msg.forward_from)
 | 
			
		||||
	end
 | 
			
		||||
	if msg.new_chat_participant then
 | 
			
		||||
		msg.new_chat_participant = utilities.enrich_user(msg.new_chat_participant)
 | 
			
		||||
	if msg.new_chat_member then
 | 
			
		||||
		msg.new_chat_member = utilities.enrich_user(msg.new_chat_member)
 | 
			
		||||
	end
 | 
			
		||||
	if msg.left_chat_participant then
 | 
			
		||||
		msg.left_chat_participant = utilities.enrich_user(msg.left_chat_participant)
 | 
			
		||||
	if msg.left_chat_member then
 | 
			
		||||
		msg.left_chat_member = utilities.enrich_user(msg.left_chat_member)
 | 
			
		||||
	end
 | 
			
		||||
	return msg
 | 
			
		||||
end
 | 
			
		||||
@@ -633,29 +583,6 @@ function scandir(directory)
 | 
			
		||||
  return t
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Returns at table of lua files inside plugins
 | 
			
		||||
function plugins_names()
 | 
			
		||||
  local files = {}
 | 
			
		||||
  for k, v in pairs(scandir("miku/plugins")) do
 | 
			
		||||
    -- Ends with .lua
 | 
			
		||||
    if (v:match(".lua$")) then
 | 
			
		||||
	  files[#files+1] = v
 | 
			
		||||
    end 
 | 
			
		||||
  end
 | 
			
		||||
  return files
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Function name explains what it does.
 | 
			
		||||
function file_exists(name)
 | 
			
		||||
  local f = io.open(name,"r")
 | 
			
		||||
  if f ~= nil then 
 | 
			
		||||
    io.close(f) 
 | 
			
		||||
    return true 
 | 
			
		||||
  else 
 | 
			
		||||
    return false 
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Returns a table with matches or nil
 | 
			
		||||
function match_pattern(pattern, text)
 | 
			
		||||
  if text then
 | 
			
		||||
@@ -794,17 +721,6 @@ function get_redis_hash(msg, var)
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- remove whitespace
 | 
			
		||||
function all_trim(s)
 | 
			
		||||
  return s:match( "^%s*(.-)%s*$" )
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function tablelength(T)
 | 
			
		||||
  local count = 0
 | 
			
		||||
  for _ in pairs(T) do count = count + 1 end
 | 
			
		||||
  return count
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function round(num, idp)
 | 
			
		||||
  if idp and idp>0 then
 | 
			
		||||
    local mult = 10^idp
 | 
			
		||||
@@ -824,6 +740,9 @@ function comma_value(amount)
 | 
			
		||||
  return formatted
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function string.starts(String,Start)
 | 
			
		||||
   return string.sub(String,1,string.len(Start))==Start
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function string.ends(str, fin)
 | 
			
		||||
  return fin=='' or string.sub(str,-string.len(fin)) == fin
 | 
			
		||||
@@ -1034,19 +953,6 @@ function makeHumanTime(totalseconds)
 | 
			
		||||
	end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function is_blacklisted(msg)
 | 
			
		||||
  _blacklist = redis:smembers("telegram:img_blacklist")
 | 
			
		||||
  local var = false
 | 
			
		||||
  for v,word in pairs(_blacklist) do
 | 
			
		||||
    if string.find(string.lower(msg), string.lower(word)) then
 | 
			
		||||
      print("Wort steht auf der Blacklist!")
 | 
			
		||||
      var = true
 | 
			
		||||
      break
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
  return var
 | 
			
		||||
end
 | 
			
		||||
  
 | 
			
		||||
function run_bash(str)
 | 
			
		||||
    local cmd = io.popen(str)
 | 
			
		||||
@@ -1098,16 +1004,6 @@ function convertNumbers(str)
 | 
			
		||||
  return str
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function url_encode(str)
 | 
			
		||||
  if (str) then
 | 
			
		||||
    str = string.gsub (str, "\n", "\r\n")
 | 
			
		||||
    str = string.gsub (str, "([^%w %-%_%.%~])",
 | 
			
		||||
        function (c) return string.format ("%%%02X", string.byte(c)) end)
 | 
			
		||||
    str = string.gsub (str, " ", "+")
 | 
			
		||||
  end
 | 
			
		||||
  return str
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function table.contains(table, element)
 | 
			
		||||
  for _, value in pairs(table) do
 | 
			
		||||
    if value == element then
 | 
			
		||||
@@ -1136,4 +1032,4 @@ function utilities.fix_utf8(str)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
return utilities
 | 
			
		||||
return utilities
 | 
			
		||||
		Reference in New Issue
	
	Block a user