From d0a728e214f32b71d1148ee3eba94b150829fe61 Mon Sep 17 00:00:00 2001
From: yago <yagoperezs@gmail.com>
Date: Wed, 24 Dec 2014 01:38:41 +0100
Subject: [PATCH 01/17] Initial test of plugins handler

---
 bot/bot.lua         | 18 ++++---------
 bot/config.json     |  1 +
 bot/utils.lua       | 29 +++++++++++++++++++++
 plugins/plugins.lua | 62 +++++++++++++++++++++++++++++++++++++++++++++
 plugins/reload.lua  | 14 ----------
 5 files changed, 97 insertions(+), 27 deletions(-)
 create mode 100644 plugins/plugins.lua
 delete mode 100644 plugins/reload.lua

diff --git a/bot/bot.lua b/bot/bot.lua
index 70f5d8b..dbe56fc 100644
--- a/bot/bot.lua
+++ b/bot/bot.lua
@@ -22,11 +22,6 @@ end
 function ok_cb(extra, success, result)
 end
 
--- Callback to remove tmp files
-function rmtmp_cb(file_path, success, result)
-   os.remove(file_path)
-end
-
 function msg_valid(msg)
   -- if msg.from.id == our_id then
   --   return true
@@ -170,15 +165,12 @@ function on_binlog_replay_end ()
   -- See plugins/ping.lua as an example for cron
 end
 
--- load all plugins in the plugins/ directory
+-- Enable plugins in config.json
 function load_plugins()
-  for k, v in pairs(scandir("plugins")) do
-    -- Load only lua files
-    if (v:match(".lua$")) then
-        print("Loading plugin", v)
-        t = loadfile("plugins/" .. v)()
-        table.insert(plugins, t)
-    end 
+  for k, v in pairs(config.enabled_plugins) do
+    print("Loading plugin", v)
+    t = loadfile("plugins/" .. v)()
+    table.insert(plugins, t)
   end
 end
 
diff --git a/bot/config.json b/bot/config.json
index feff6fc..52efcf4 100644
--- a/bot/config.json
+++ b/bot/config.json
@@ -1,4 +1,5 @@
 {
+   "enabled_plugins": [ "plugins.lua", "echo.lua", "hello.lua" ],
        "rmtmp_delay": 20,
     "google_api_key": "",
           "log_file": "/var/www/html/log.txt",
diff --git a/bot/utils.lua b/bot/utils.lua
index 3f774b8..26da8ac 100644
--- a/bot/utils.lua
+++ b/bot/utils.lua
@@ -67,6 +67,11 @@ function download_to_file( url , noremove )
   return file_path
 end
 
+-- Callback to remove a file
+function rmtmp_cb(file_path, success, result)
+   os.remove(file_path)
+end
+
 function vardump(value, depth, key)
   local linePrefix = ""
   local spaces = ""
@@ -133,10 +138,34 @@ function is_sudo(msg)
    return var
 end
 
+-- Returns the name of the sender
 function get_name(msg)
    local name = msg.from.first_name
    if name == nil then
       name = msg.from.id
    end
    return name
+end
+
+-- Returns at table of lua files inside plugins
+function plugins_names( )
+  local files = {}
+  for k, v in pairs(scandir("plugins")) do
+    -- Ends with .lua
+    if (v:match(".lua$")) then
+      table.insert(files, 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
\ No newline at end of file
diff --git a/plugins/plugins.lua b/plugins/plugins.lua
new file mode 100644
index 0000000..5fc4a2b
--- /dev/null
+++ b/plugins/plugins.lua
@@ -0,0 +1,62 @@
+function enable_plugin( filename )
+	-- Checks if file exists
+	if file_exists('plugins/'..filename) then
+		-- Add to the config table
+		table.insert(config.enabled_plugins, filename)
+		-- Reload the plugins
+		reload_plugins( )
+	else
+		return 'Plugin does not exists'
+	end
+end
+
+function reload_plugins( )
+	plugins = {}
+	load_plugins()
+	return list_plugins(true)
+end
+
+function list_plugins(only_enabled)
+	local text = ''
+	for k, v in pairs( plugins_names( )) do
+		--  ✔ enabled, ❌ disabled
+		local status = '❌'
+		-- Check if is enabled
+		for k2, v2 in pairs(config.enabled_plugins) do
+			if v == v2 then 
+				status = '✔' 
+			end
+		end
+		if not only_enabled or status == '✔' then
+			text = text..v..' '..status..'\n'
+		end
+	end
+	return text
+end
+
+function run(msg, matches)
+	-- Show the available plugins 
+	if matches[1] == '!plugins' then
+		return list_plugins()
+	end
+	-- Reload all the plugins!
+	if matches[1] == 'reload' then
+		return reload_plugins(true)
+	end
+	-- Enable a plugin
+	if matches[1] == 'enable' then
+		print("enable: "..matches[2])
+		return enable_plugin(matches[2])
+	end
+end
+
+return {
+	description = "Enable / Disable plugins", 
+	usage = "!plugins",
+	patterns = {
+		"^!plugins$",
+		"^!plugins (enable) ([%w%.]+)$",
+		"^!plugins (reload)$"
+		}, 
+	run = run 
+}
\ No newline at end of file
diff --git a/plugins/reload.lua b/plugins/reload.lua
deleted file mode 100644
index f6bfaef..0000000
--- a/plugins/reload.lua
+++ /dev/null
@@ -1,14 +0,0 @@
-
-function run(msg, matches)
-  plugins = {}
-  load_plugins()
-  return 'Plugins reloaded'
-end
-
-return {
-    description = "Reloads bot plugins", 
-    usage = "!reload",
-    patterns = {"^!reload$"}, 
-    run = run 
-}
-

From 03e52b1d413418b3855ef7cc1a7788a234ab5c45 Mon Sep 17 00:00:00 2001
From: yago <yagoperezs@gmail.com>
Date: Wed, 24 Dec 2014 12:44:57 +0100
Subject: [PATCH 02/17] disable plugins and some checks

---
 plugins/plugins.lua | 70 +++++++++++++++++++++++++++++++++++++--------
 1 file changed, 58 insertions(+), 12 deletions(-)

diff --git a/plugins/plugins.lua b/plugins/plugins.lua
index 5fc4a2b..3525ee9 100644
--- a/plugins/plugins.lua
+++ b/plugins/plugins.lua
@@ -1,21 +1,61 @@
 function enable_plugin( filename )
-	-- Checks if file exists
-	if file_exists('plugins/'..filename) then
+	-- Check if plugin is enabled
+	if plugin_enabled(filename) then
+		return 'Plugin '..filename..' is enabled'
+	end
+	-- Checks if plugin exists
+	if plugin_exists(filename) then
 		-- Add to the config table
 		table.insert(config.enabled_plugins, filename)
 		-- Reload the plugins
-		reload_plugins( )
+		return reload_plugins( )
 	else
-		return 'Plugin does not exists'
+		return 'Plugin '..filename..' does not exists'
 	end
 end
 
+function disable_plugin( filename )
+	-- Check if plugins exists
+	if not plugin_exists(filename) then
+		return 'Plugin '..filename..' does not exists'
+	end
+	local k = plugin_enabled(filename)
+	-- Check if plugin is enabled
+	if not k then
+		return 'Plugin '..filename..' not enabled'
+	end
+	-- Disable and reload
+	table.remove(config.enabled_plugins, k)
+	return reload_plugins(true)		
+end
+
 function reload_plugins( )
 	plugins = {}
 	load_plugins()
 	return list_plugins(true)
 end
 
+-- Retruns the key (index) in the config.enabled_plugins table
+function plugin_enabled( name )
+	for k,v in pairs(config.enabled_plugins) do
+		if name == v then
+			return k
+		end
+	end
+	-- If not found
+	return false
+end
+
+-- Returns true if file exists in plugins folder
+function plugin_exists( name )
+  for k,v in pairs(plugins_names()) do
+    if name == v then
+      return true
+    end
+  end
+  return false
+end
+
 function list_plugins(only_enabled)
 	local text = ''
 	for k, v in pairs( plugins_names( )) do
@@ -39,24 +79,30 @@ function run(msg, matches)
 	if matches[1] == '!plugins' then
 		return list_plugins()
 	end
-	-- Reload all the plugins!
-	if matches[1] == 'reload' then
-		return reload_plugins(true)
-	end
 	-- Enable a plugin
 	if matches[1] == 'enable' then
 		print("enable: "..matches[2])
 		return enable_plugin(matches[2])
 	end
+	-- Disable a plugin
+	if matches[1] == 'disable' then
+		print("disable: "..matches[2])
+		return disable_plugin(matches[2])
+	end
+	-- Reload all the plugins!
+	if matches[1] == 'reload' then
+		return reload_plugins(true)
+	end
 end
 
 return {
-	description = "Enable / Disable plugins", 
-	usage = "!plugins",
+	description = "Enables, disables and reloads plugins", 
+	usage = "!plugins, !plugins enable [plugin.lua], !plugins disable [plugin.lua], !plugins reload",
 	patterns = {
 		"^!plugins$",
-		"^!plugins (enable) ([%w%.]+)$",
-		"^!plugins (reload)$"
+		"^!plugins? (enable) (.*)$",
+		"^!plugins? (disable) (.*)$",
+		"^!plugins? (reload)$"
 		}, 
 	run = run 
 }
\ No newline at end of file

From 4fbfd8556f56b35d04970a3696ace16a69dab15c Mon Sep 17 00:00:00 2001
From: yago <yagoperezs@gmail.com>
Date: Wed, 24 Dec 2014 19:19:16 +0100
Subject: [PATCH 03/17] Updated JSON.lua

---
 bot/JSON.lua | 238 +++++++++++++++++++++++++++++++++++++++------------
 1 file changed, 185 insertions(+), 53 deletions(-)

diff --git a/bot/JSON.lua b/bot/JSON.lua
index 8723771..5f11425 100644
--- a/bot/JSON.lua
+++ b/bot/JSON.lua
@@ -14,13 +14,13 @@
 -- the web-page links above, and the 'AUTHOR_NOTE' string below are
 -- maintained. Enjoy.
 --
-local VERSION = 20140920.13  -- version history at end of file
-local AUTHOR_NOTE = "-[ JSON.lua package by Jeffrey Friedl (http://regex.info/blog/lua/json) version 20140920.13 ]-"
+local VERSION = 20141223.14 -- version history at end of file
+local AUTHOR_NOTE = "-[ JSON.lua package by Jeffrey Friedl (http://regex.info/blog/lua/json) version 20141223.14 ]-"
 
 --
 -- The 'AUTHOR_NOTE' variable exists so that information about the source
--- of the package is maintained even in compiled versions. It's included in
--- OBJDEF mostly to quiet warnings about unused variables.
+-- of the package is maintained even in compiled versions. It's also
+-- included in OBJDEF below mostly to quiet warnings about unused variables.
 --
 local OBJDEF = {
    VERSION      = VERSION,
@@ -33,7 +33,7 @@ local OBJDEF = {
 -- http://www.json.org/
 --
 --
---   JSON = (loadfile "JSON.lua")() -- one-time load of the routines
+--   JSON = assert(loadfile "JSON.lua")() -- one-time load of the routines
 --
 --   local lua_value = JSON:decode(raw_json_text)
 --
@@ -41,9 +41,11 @@ local OBJDEF = {
 --   local pretty_json_text = JSON:encode_pretty(lua_table_or_value) -- "pretty printed" version for human readability
 --
 --
--- DECODING
 --
---   JSON = (loadfile "JSON.lua")() -- one-time load of the routines
+-- DECODING (from a JSON string to a Lua table)
+--
+--
+--   JSON = assert(loadfile "JSON.lua")() -- one-time load of the routines
 --
 --   local lua_value = JSON:decode(raw_json_text)
 --
@@ -58,22 +60,27 @@ local OBJDEF = {
 --     { "Larry", "Curly", "Moe" }
 --
 --
---   The encode and decode routines accept an optional second argument, "etc", which is not used
---   during encoding or decoding, but upon error is passed along to error handlers. It can be of any
---   type (including nil).
+--   The encode and decode routines accept an optional second argument,
+--   "etc", which is not used during encoding or decoding, but upon error
+--   is passed along to error handlers. It can be of any type (including nil).
+--
+--
+--
+-- ERROR HANDLING
 --
 --   With most errors during decoding, this code calls
 --
 --      JSON:onDecodeError(message, text, location, etc)
 --
---   with a message about the error, and if known, the JSON text being parsed and the byte count
---   where the problem was discovered. You can replace the default JSON:onDecodeError() with your
---   own function.
+--   with a message about the error, and if known, the JSON text being
+--   parsed and the byte count where the problem was discovered. You can
+--   replace the default JSON:onDecodeError() with your own function.
 --
---   The default onDecodeError() merely augments the message with data about the text and the
---   location if known (and if a second 'etc' argument had been provided to decode(), its value is
---   tacked onto the message as well), and then calls JSON.assert(), which itself defaults to Lua's
---   built-in assert(), and can also be overridden.
+--   The default onDecodeError() merely augments the message with data
+--   about the text and the location if known (and if a second 'etc'
+--   argument had been provided to decode(), its value is tacked onto the
+--   message as well), and then calls JSON.assert(), which itself defaults
+--   to Lua's built-in assert(), and can also be overridden.
 --
 --   For example, in an Adobe Lightroom plugin, you might use something like
 --
@@ -95,9 +102,10 @@ local OBJDEF = {
 --
 --      JSON:onDecodeOfHTMLError(message, text, nil, etc)
 --
---   The use of the fourth 'etc' argument allows stronger coordination between decoding and error
---   reporting, especially when you provide your own error-handling routines. Continuing with the
---   the Adobe Lightroom plugin example:
+--   The use of the fourth 'etc' argument allows stronger coordination
+--   between decoding and error reporting, especially when you provide your
+--   own error-handling routines. Continuing with the the Adobe Lightroom
+--   plugin example:
 --
 --          function JSON:onDecodeError(message, text, location, etc)
 --             local note = "Internal Error: invalid JSON data"
@@ -121,42 +129,136 @@ local OBJDEF = {
 --
 --
 --
-
+--
 -- DECODING AND STRICT TYPES
 --
---   Because both JSON objects and JSON arrays are converted to Lua tables, it's not normally
---   possible to tell which a JSON type a particular Lua table was derived from, or guarantee
---   decode-encode round-trip equivalency.
+--   Because both JSON objects and JSON arrays are converted to Lua tables,
+--   it's not normally possible to tell which original JSON type a
+--   particular Lua table was derived from, or guarantee decode-encode
+--   round-trip equivalency.
 --
 --   However, if you enable strictTypes, e.g.
 --
---      JSON = (loadfile "JSON.lua")() --load the routines
+--      JSON = assert(loadfile "JSON.lua")() --load the routines
 --      JSON.strictTypes = true
 --
---   then the Lua table resulting from the decoding of a JSON object or JSON array is marked via Lua
---   metatable, so that when re-encoded with JSON:encode() it ends up as the appropriate JSON type.
+--   then the Lua table resulting from the decoding of a JSON object or
+--   JSON array is marked via Lua metatable, so that when re-encoded with
+--   JSON:encode() it ends up as the appropriate JSON type.
 --
---   (This is not the default because other routines may not work well with tables that have a
---   metatable set, for example, Lightroom API calls.)
+--   (This is not the default because other routines may not work well with
+--   tables that have a metatable set, for example, Lightroom API calls.)
 --
 --
--- ENCODING
+-- ENCODING (from a lua table to a JSON string)
 --
---   JSON = (loadfile "JSON.lua")() -- one-time load of the routines
+--   JSON = assert(loadfile "JSON.lua")() -- one-time load of the routines
 --
 --   local raw_json_text    = JSON:encode(lua_table_or_value)
 --   local pretty_json_text = JSON:encode_pretty(lua_table_or_value) -- "pretty printed" version for human readability
-
+--   local custom_pretty    = JSON:encode(lua_table_or_value, etc, { pretty = true, indent = "|  ", align_keys = false })
+--
 --   On error during encoding, this code calls:
 --
---    JSON:onEncodeError(message, etc)
+--     JSON:onEncodeError(message, etc)
 --
 --   which you can override in your local JSON object.
 --
---   If the Lua table contains both string and numeric keys, it fits neither JSON's
---   idea of an object, nor its idea of an array. To get around this, when any string
---   key exists (or when non-positive numeric keys exist), numeric keys are converted
---   to strings.
+--   The 'etc' in the error call is the second argument to encode()
+--   and encode_pretty(), or nil if it wasn't provided.
+--
+--
+-- PRETTY-PRINTING
+--
+--   An optional third argument, a table of options, allows a bit of
+--   configuration about how the encoding takes place:
+--
+--     pretty = JSON:encode(val, etc, {
+--                                       pretty = true,      -- if false, no other options matter
+--                                       indent = "   ",     -- this provides for a three-space indent per nesting level
+--                                       align_keys = false, -- see below
+--                                     })
+--
+--   encode() and encode_pretty() are identical except that encode_pretty()
+--   provides a default options table if none given in the call:
+--
+--       { pretty = true, align_keys = false, indent = "  " }
+--
+--   For example, if
+--
+--      JSON:encode(data)
+--
+--   produces:
+--
+--      {"city":"Kyoto","climate":{"avg_temp":16,"humidity":"high","snowfall":"minimal"},"country":"Japan","wards":11}
+--
+--   then
+--
+--      JSON:encode_pretty(data)
+--
+--   produces:
+--
+--      {
+--        "city": "Kyoto",
+--        "climate": {
+--          "avg_temp": 16,
+--          "humidity": "high",
+--          "snowfall": "minimal"
+--        },
+--        "country": "Japan",
+--        "wards": 11
+--      }
+--
+--   The following three lines return identical results:
+--       JSON:encode_pretty(data)
+--       JSON:encode_pretty(data, nil, { pretty = true, align_keys = false, indent = "  " })
+--       JSON:encode       (data, nil, { pretty = true, align_keys = false, indent = "  " })
+--
+--   An example of setting your own indent string:
+--
+--     JSON:encode_pretty(data, nil, { pretty = true, indent = "|    " })
+--
+--   produces:
+--
+--      {
+--      |    "city": "Kyoto",
+--      |    "climate": {
+--      |    |    "avg_temp": 16,
+--      |    |    "humidity": "high",
+--      |    |    "snowfall": "minimal"
+--      |    },
+--      |    "country": "Japan",
+--      |    "wards": 11
+--      }
+--
+--   An example of setting align_keys to true:
+--
+--     JSON:encode_pretty(data, nil, { pretty = true, indent = "  ", align_keys = true })
+--  
+--   produces:
+--   
+--      {
+--           "city": "Kyoto",
+--        "climate": {
+--                     "avg_temp": 16,
+--                     "humidity": "high",
+--                     "snowfall": "minimal"
+--                   },
+--        "country": "Japan",
+--          "wards": 11
+--      }
+--
+--   which I must admit is kinda ugly, sorry. This was the default for
+--   encode_pretty() prior to version 20141223.14.
+--
+--
+--  AMBIGUOUS SITUATIONS DURING THE ENCODING
+--
+--   During the encode, if a Lua table being encoded contains both string
+--   and numeric keys, it fits neither JSON's idea of an object, nor its
+--   idea of an array. To get around this, when any string key exists (or
+--   when non-positive numeric keys exist), numeric keys are converted to
+--   strings.
 --
 --   For example, 
 --     JSON:encode({ "one", "two", "three", SOMESTRING = "some string" }))
@@ -165,6 +267,9 @@ local OBJDEF = {
 --
 --   To prohibit this conversion and instead make it an error condition, set
 --      JSON.noKeyConversion = true
+--
+
+
 
 
 --
@@ -181,6 +286,9 @@ local OBJDEF = {
 --
 ---------------------------------------------------------------------------
 
+local default_pretty_indent  = "  "
+local default_pretty_options = { pretty = true, align_keys = false, indent = default_pretty_indent }
+
 local isArray  = { __tostring = function() return "JSON array"  end }    isArray.__index  = isArray
 local isObject = { __tostring = function() return "JSON object" end }    isObject.__index = isObject
 
@@ -692,8 +800,13 @@ end
 --
 -- Encode
 --
+-- 'options' is nil, or a table with possible keys:
+--    pretty            -- if true, return a pretty-printed version
+--    indent            -- a string (usually of spaces) used to indent each nested level
+--    align_keys        -- if true, align all the keys when formatting a table
+--
 local encode_value -- must predeclare because it calls itself
-function encode_value(self, value, parents, etc, indent) -- non-nil indent means pretty-printing
+function encode_value(self, value, parents, etc, options, indent)
 
    if value == nil then
       return 'null'
@@ -739,6 +852,13 @@ function encode_value(self, value, parents, etc, indent) -- non-nil indent means
       --
       local T = value
 
+      if type(options) ~= 'table' then
+         options = {}
+      end
+      if type(indent) ~= 'string' then
+         indent = ""
+      end
+
       if parents[T] then
          self:onEncodeError("table " .. tostring(T) .. " is a child of itself", etc)
       else
@@ -754,13 +874,13 @@ function encode_value(self, value, parents, etc, indent) -- non-nil indent means
          --
          local ITEMS = { }
          for i = 1, maximum_number_key do
-            table.insert(ITEMS, encode_value(self, T[i], parents, etc, indent))
+            table.insert(ITEMS, encode_value(self, T[i], parents, etc, options, indent))
          end
 
-         if indent then
+         if options.pretty then
             result_value = "[ " .. table.concat(ITEMS, ", ") .. " ]"
          else
-            result_value = "[" .. table.concat(ITEMS, ",") .. "]"
+            result_value = "["  .. table.concat(ITEMS, ",")  .. "]"
          end
 
       elseif object_keys then
@@ -769,22 +889,24 @@ function encode_value(self, value, parents, etc, indent) -- non-nil indent means
          --
          local TT = map or T
 
-         if indent then
+         if options.pretty then
 
             local KEYS = { }
             local max_key_length = 0
             for _, key in ipairs(object_keys) do
-               local encoded = encode_value(self, tostring(key), parents, etc, "")
-               max_key_length = math.max(max_key_length, #encoded)
+               local encoded = encode_value(self, tostring(key), parents, etc, options, indent)
+               if options.align_keys then
+                  max_key_length = math.max(max_key_length, #encoded)
+               end
                table.insert(KEYS, encoded)
             end
-            local key_indent = indent .. "    "
-            local subtable_indent = indent .. string.rep(" ", max_key_length + 2 + 4)
+            local key_indent = indent .. tostring(options.indent or "")
+            local subtable_indent = key_indent .. string.rep(" ", max_key_length) .. (options.align_keys and "  " or "")
             local FORMAT = "%s%" .. string.format("%d", max_key_length) .. "s: %s"
 
             local COMBINED_PARTS = { }
             for i, key in ipairs(object_keys) do
-               local encoded_val = encode_value(self, TT[key], parents, etc, subtable_indent)
+               local encoded_val = encode_value(self, TT[key], parents, etc, options, subtable_indent)
                table.insert(COMBINED_PARTS, string.format(FORMAT, key_indent, KEYS[i], encoded_val))
             end
             result_value = "{\n" .. table.concat(COMBINED_PARTS, ",\n") .. "\n" .. indent .. "}"
@@ -793,8 +915,8 @@ function encode_value(self, value, parents, etc, indent) -- non-nil indent means
 
             local PARTS = { }
             for _, key in ipairs(object_keys) do
-               local encoded_val = encode_value(self, TT[key],       parents, etc, indent)
-               local encoded_key = encode_value(self, tostring(key), parents, etc, indent)
+               local encoded_val = encode_value(self, TT[key],       parents, etc, options, indent)
+               local encoded_key = encode_value(self, tostring(key), parents, etc, options, indent)
                table.insert(PARTS, string.format("%s:%s", encoded_key, encoded_val))
             end
             result_value = "{" .. table.concat(PARTS, ",") .. "}"
@@ -813,18 +935,18 @@ function encode_value(self, value, parents, etc, indent) -- non-nil indent means
 end
 
 
-function OBJDEF:encode(value, etc)
+function OBJDEF:encode(value, etc, options)
    if type(self) ~= 'table' or self.__index ~= OBJDEF then
       OBJDEF:onEncodeError("JSON:encode must be called in method format", etc)
    end
-   return encode_value(self, value, {}, etc, nil)
+   return encode_value(self, value, {}, etc, options or nil)
 end
 
-function OBJDEF:encode_pretty(value, etc)
+function OBJDEF:encode_pretty(value, etc, options)
    if type(self) ~= 'table' or self.__index ~= OBJDEF then
       OBJDEF:onEncodeError("JSON:encode_pretty must be called in method format", etc)
    end
-   return encode_value(self, value, {}, etc, "")
+   return encode_value(self, value, {}, etc, options or default_pretty_options)
 end
 
 function OBJDEF.__tostring()
@@ -850,6 +972,16 @@ return OBJDEF:new()
 --
 -- Version history:
 --
+--   20141223.14   The encode_pretty() routine produced fine results for small datasets, but isn't really
+--                 appropriate for anything large, so with help from Alex Aulbach I've made the encode routines
+--                 more flexible, and changed the default encode_pretty() to be more generally useful.
+--
+--                 Added a third 'options' argument to the encode() and encode_pretty() routines, to control
+--                 how the encoding takes place.
+--
+--                 Updated docs to add assert() call to the loadfile() line, just as good practice so that
+--                 if there is a problem loading JSON.lua, the appropriate error message will percolate up.
+--
 --   20140920.13   Put back (in a way that doesn't cause warnings about unused variables) the author string,
 --                 so that the source of the package, and its version number, are visible in compiled copies.
 --

From d40346b4810e20ad2af849dff938f9904822bdd9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20Mun=CC=83oz?= <dmunoz@agnitio-corp.com>
Date: Fri, 26 Dec 2014 12:13:27 +0100
Subject: [PATCH 04/17] lexical replacements

---
 bot/bot.lua     | 32 +++++++++++++++++++++++++++++---
 plugins/get.lua | 29 ++++++++++++++++++++++++++++-
 2 files changed, 57 insertions(+), 4 deletions(-)

diff --git a/bot/bot.lua b/bot/bot.lua
index 70f5d8b..0e1e19d 100644
--- a/bot/bot.lua
+++ b/bot/bot.lua
@@ -42,6 +42,25 @@ function msg_valid(msg)
   end
 end
 
+function do_lex(msg, text)
+  local mutated = true
+  while mutated do
+    mutated = false
+    for name, desc in pairs(plugins) do
+      if (desc.lex ~= nil) then
+        result = desc.lex(msg, text)
+        if (result ~= nil) then
+          -- print ("Mutating to " .. result)
+          text = result
+          mutated = true
+        end
+      end
+    end
+  end
+  -- print("Text mutated to " .. text)
+  return text
+end
+
 -- Where magic happens
 function do_action(msg)
   local receiver = get_receiver(msg)
@@ -52,18 +71,25 @@ function do_action(msg)
      text = '['..msg.media.type..']'
   end
   -- print("Received msg", text)
+
+  text = do_lex(msg, text)
+
   for name, desc in pairs(plugins) do
     -- print("Trying module", name)
     for k, pattern in pairs(desc.patterns) do
       -- print("Trying", text, "against", pattern)
       matches = { string.match(text, pattern) }
       if matches[1] then
-        print("  matches",pattern)
+        -- print("  matches",pattern)
         if desc.run ~= nil then
           result = desc.run(msg, matches)
-          print("  sending", result)
+          -- print("  sending", result)
           if (result) then
-            _send_msg(receiver, result)
+            local result2 = do_lex(msg, result)
+            if (result2 == nil) then 
+              result2 = result
+            end
+            _send_msg(receiver, result2)
             return
           end
         end
diff --git a/plugins/get.lua b/plugins/get.lua
index d9bad27..7f4631a 100644
--- a/plugins/get.lua
+++ b/plugins/get.lua
@@ -10,6 +10,17 @@ else
   _values = json:decode(c)
 end
 
+function fetch_value(chat, value_name)
+  if (_values[chat] == nil) then
+    return nil
+  end
+  if (value_name == nil ) then
+    return nil
+  end 
+  local value = _values[chat][value_name]
+  return value
+end
+
 function get_value(chat, value_name)
 
   -- If chat values is empty
@@ -40,11 +51,27 @@ function run(msg, matches)
    return get_value(chat_id, matches[1])
 end
 
+function lex(msg, text)
+  local chat_id = tostring(msg.to.id)
+  local s, e = text:find("%$%a+")
+  if (s == nil) then 
+    return nil
+  end
+  local var = text:sub(s + 1, e)
+  local value = fetch_value(chat_id, var)
+  if (value == nil) then
+    value = "(unknown value " .. var .. ")"
+  end
+  return text:sub(0, s - 1) .. value .. text:sub(e + 1)
+end
+
 return {
     description = "retrieves variables saved with !set", 
     usage = "!get (value_name)",
     patterns = {
       "^!get (%a+)$",
       "^!get$"}, 
-    run = run 
+    run = run,
+    lex = lex
 }
+

From e811ea8a5295cefbe3b4cec6a6bee7b25a9ce4aa Mon Sep 17 00:00:00 2001
From: yago <yagoperezs@gmail.com>
Date: Wed, 31 Dec 2014 17:14:48 +0100
Subject: [PATCH 05/17] Enable / Diable plugins. libs folder. Generate
 config.lua with serpent, defined enabled_plugins. Plugins config, is inside
 them, not in config.lua.

---
 .gitignore               |   3 +-
 bot/bot.lua              | 108 ++++++++++++++++++++++-----------
 bot/config.json          |  14 -----
 bot/utils.lua            |   5 +-
 {bot => libs}/JSON.lua   |   0
 libs/serpent.lua         | 128 +++++++++++++++++++++++++++++++++++++++
 plugins/location.lua     |   4 +-
 plugins/plugins.lua      |  26 ++++----
 plugins/time.lua         |   3 +-
 plugins/twitter.lua      |   8 +--
 plugins/twitter_send.lua |   8 +--
 11 files changed, 233 insertions(+), 74 deletions(-)
 delete mode 100644 bot/config.json
 rename {bot => libs}/JSON.lua (100%)
 create mode 100644 libs/serpent.lua

diff --git a/.gitignore b/.gitignore
index d6d2cd0..76174e7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
-res/
\ No newline at end of file
+res/
+bot/config.lua
\ No newline at end of file
diff --git a/bot/bot.lua b/bot/bot.lua
index dbe56fc..226c084 100644
--- a/bot/bot.lua
+++ b/bot/bot.lua
@@ -1,10 +1,11 @@
 http = require("socket.http")
 https = require("ssl.https")
 URL = require("socket.url")
-json = (loadfile "./bot/JSON.lua")()
+json = (loadfile "./libs/JSON.lua")()
+serpent = (loadfile "./libs/serpent.lua")()
 require("./bot/utils")
 
-VERSION = 'v0.7.7'
+VERSION = '0.8.0'
 
 function on_msg_receive (msg)
   vardump(msg)
@@ -22,10 +23,22 @@ end
 function ok_cb(extra, success, result)
 end
 
+function on_binlog_replay_end ()
+  started = 1
+  -- Uncomment the line to enable cron plugins.
+  -- postpone (cron_plugins, false, 5.0)
+  -- See plugins/ping.lua as an example for cron
+
+  _config = load_config()
+  _users = load_user_stats()
+
+  -- load plugins
+  plugins = {}
+  load_plugins()
+end
+
 function msg_valid(msg)
-  -- if msg.from.id == our_id then
-  --   return true
-  -- end
+  -- Dont process outgoing messages
   if msg.out then
     return false
   end
@@ -83,19 +96,60 @@ function _send_msg( destination, text)
   end
 end
 
+-- Save the content of _config to config.lua
+function save_config( )
+  file = io.open('./bot/config.lua', 'w+')
+  local serialized = serpent.block(_config, {
+    comment = false,
+    name = "config"
+  })
+  file:write(serialized)
+  file:close()
+end
 
-function load_config()
-   local f = assert(io.open('./bot/config.json', "r"))
-   local c = f:read "*a"
-   local config = json:decode(c)
-   if config.sh_enabled then 
-      print ("!sh command is enabled")
-      for v,user in pairs(config.sudo_users) do
-         print("Allowed user: " .. user)
-      end
-   end
-   f:close()
-   return config
+
+function load_config( )
+  local f = io.open('./bot/config.lua', "r")
+  -- If config.lua doesnt exists
+  if not f then
+    print ("Created new config file: bot/config.lua")
+    create_config()
+  end
+  f:close()
+  local config = loadfile ("./bot/config.lua")()
+  for v,user in pairs(config.sudo_users) do
+    print("Allowed user: " .. user)
+  end
+  return config
+end
+
+-- Create a basic config.json file and saves it.
+function create_config( )
+  -- A simple config with basic plugins and ourserves as priviled user
+  config = {
+    enabled_plugins = {
+      "9gag", 
+      "echo", 
+      "get",
+      "set",
+      "images",
+      "img_google",
+      "location",
+      "media",
+      "plugins",
+      "stats",
+      "time",
+      "version",
+      "youtube" },
+    sudo_users = {our_id}  
+  }
+  file = io.open('./bot/config.lua', 'w+')
+  local serialized = serpent.block(config, {
+    comment = false,
+    name = "config"
+  })
+  file:write(serialized)
+  file:close()
 end
 
 function update_user_stats(msg)
@@ -158,18 +212,11 @@ end
 function on_get_difference_end ()
 end
 
-function on_binlog_replay_end ()
-  started = 1
-  -- Uncomment the line to enable cron plugins.
-  -- postpone (cron_plugins, false, 5.0)
-  -- See plugins/ping.lua as an example for cron
-end
-
 -- Enable plugins in config.json
 function load_plugins()
-  for k, v in pairs(config.enabled_plugins) do
+  for k, v in pairs(_config.enabled_plugins) do
     print("Loading plugin", v)
-    t = loadfile("plugins/" .. v)()
+    t = loadfile("plugins/"..v..'.lua')()
     table.insert(plugins, t)
   end
 end
@@ -190,11 +237,4 @@ end
 
 -- Start and load values
 our_id = 0
-now = os.time()
-
-config = load_config()
-_users = load_user_stats()
-
--- load plugins
-plugins = {}
-load_plugins()
\ No newline at end of file
+now = os.time()
\ No newline at end of file
diff --git a/bot/config.json b/bot/config.json
deleted file mode 100644
index 52efcf4..0000000
--- a/bot/config.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-   "enabled_plugins": [ "plugins.lua", "echo.lua", "hello.lua" ],
-       "rmtmp_delay": 20,
-    "google_api_key": "",
-          "log_file": "/var/www/html/log.txt",
-        "sh_enabled": false,
-        "sudo_users": [ 0, 1 ],
-           "twitter": {
-                                 "access_token": "",
-                          "access_token_secret": "",
-                                 "consumer_key": "",
-                              "consumer_secret": ""
-                      }
-}
diff --git a/bot/utils.lua b/bot/utils.lua
index 26da8ac..81079e4 100644
--- a/bot/utils.lua
+++ b/bot/utils.lua
@@ -61,7 +61,8 @@ function download_to_file( url , noremove )
   file:close()
 
   if noremove == nil then
-     postpone(rmtmp_cb, file_path, config.rmtmp_delay)
+    print(file_path.."will be removed in 20 seconds")
+    postpone(rmtmp_cb, file_path, 20)
   end
 
   return file_path
@@ -130,7 +131,7 @@ end
 function is_sudo(msg)
    local var = false
    -- Check users id in config 
-   for v,user in pairs(config.sudo_users) do 
+   for v,user in pairs(_config.sudo_users) do 
       if user == msg.from.id then 
          var = true 
       end
diff --git a/bot/JSON.lua b/libs/JSON.lua
similarity index 100%
rename from bot/JSON.lua
rename to libs/JSON.lua
diff --git a/libs/serpent.lua b/libs/serpent.lua
new file mode 100644
index 0000000..dd08b7a
--- /dev/null
+++ b/libs/serpent.lua
@@ -0,0 +1,128 @@
+local n, v = "serpent", 0.272 -- (C) 2012-13 Paul Kulchenko; MIT License
+local c, d = "Paul Kulchenko", "Lua serializer and pretty printer"
+local snum = {[tostring(1/0)]='1/0 --[[math.huge]]',[tostring(-1/0)]='-1/0 --[[-math.huge]]',[tostring(0/0)]='0/0'}
+local badtype = {thread = true, userdata = true, cdata = true}
+local keyword, globals, G = {}, {}, (_G or _ENV)
+for _,k in ipairs({'and', 'break', 'do', 'else', 'elseif', 'end', 'false',
+  'for', 'function', 'goto', 'if', 'in', 'local', 'nil', 'not', 'or', 'repeat',
+  'return', 'then', 'true', 'until', 'while'}) do keyword[k] = true end
+for k,v in pairs(G) do globals[v] = k end -- build func to name mapping
+for _,g in ipairs({'coroutine', 'debug', 'io', 'math', 'string', 'table', 'os'}) do
+  for k,v in pairs(G[g] or {}) do globals[v] = g..'.'..k end end
+
+local function s(t, opts)
+  local name, indent, fatal, maxnum = opts.name, opts.indent, opts.fatal, opts.maxnum
+  local sparse, custom, huge = opts.sparse, opts.custom, not opts.nohuge
+  local space, maxl = (opts.compact and '' or ' '), (opts.maxlevel or math.huge)
+  local iname, comm = '_'..(name or ''), opts.comment and (tonumber(opts.comment) or math.huge)
+  local seen, sref, syms, symn = {}, {'local '..iname..'={}'}, {}, 0
+  local function gensym(val) return '_'..(tostring(tostring(val)):gsub("[^%w]",""):gsub("(%d%w+)",
+    -- tostring(val) is needed because __tostring may return a non-string value
+    function(s) if not syms[s] then symn = symn+1; syms[s] = symn end return syms[s] end)) end
+  local function safestr(s) return type(s) == "number" and (huge and snum[tostring(s)] or s)
+    or type(s) ~= "string" and tostring(s) -- escape NEWLINE/010 and EOF/026
+    or ("%q"):format(s):gsub("\010","n"):gsub("\026","\\026") end
+  local function comment(s,l) return comm and (l or 0) < comm and ' --[['..tostring(s)..']]' or '' end
+  local function globerr(s,l) return globals[s] and globals[s]..comment(s,l) or not fatal
+    and safestr(select(2, pcall(tostring, s))) or error("Can't serialize "..tostring(s)) end
+  local function safename(path, name) -- generates foo.bar, foo[3], or foo['b a r']
+    local n = name == nil and '' or name
+    local plain = type(n) == "string" and n:match("^[%l%u_][%w_]*$") and not keyword[n]
+    local safe = plain and n or '['..safestr(n)..']'
+    return (path or '')..(plain and path and '.' or '')..safe, safe end
+  local alphanumsort = type(opts.sortkeys) == 'function' and opts.sortkeys or function(k, o, n) -- k=keys, o=originaltable, n=padding
+    local maxn, to = tonumber(n) or 12, {number = 'a', string = 'b'}
+    local function padnum(d) return ("%0"..maxn.."d"):format(d) end
+    table.sort(k, function(a,b)
+      -- sort numeric keys first: k[key] is not nil for numerical keys
+      return (k[a] ~= nil and 0 or to[type(a)] or 'z')..(tostring(a):gsub("%d+",padnum))
+           < (k[b] ~= nil and 0 or to[type(b)] or 'z')..(tostring(b):gsub("%d+",padnum)) end) end
+  local function val2str(t, name, indent, insref, path, plainindex, level)
+    local ttype, level, mt = type(t), (level or 0), getmetatable(t)
+    local spath, sname = safename(path, name)
+    local tag = plainindex and
+      ((type(name) == "number") and '' or name..space..'='..space) or
+      (name ~= nil and sname..space..'='..space or '')
+    if seen[t] then -- already seen this element
+      sref[#sref+1] = spath..space..'='..space..seen[t]
+      return tag..'nil'..comment('ref', level) end
+    if type(mt) == 'table' and (mt.__serialize or mt.__tostring) then -- knows how to serialize itself
+      seen[t] = insref or spath
+      if mt.__serialize then t = mt.__serialize(t) else t = tostring(t) end
+      ttype = type(t) end -- new value falls through to be serialized
+    if ttype == "table" then
+      if level >= maxl then return tag..'{}'..comment('max', level) end
+      seen[t] = insref or spath
+      if next(t) == nil then return tag..'{}'..comment(t, level) end -- table empty
+      local maxn, o, out = math.min(#t, maxnum or #t), {}, {}
+      for key = 1, maxn do o[key] = key end
+      if not maxnum or #o < maxnum then
+        local n = #o -- n = n + 1; o[n] is much faster than o[#o+1] on large tables
+        for key in pairs(t) do if o[key] ~= key then n = n + 1; o[n] = key end end end
+      if maxnum and #o > maxnum then o[maxnum+1] = nil end
+      if opts.sortkeys and #o > maxn then alphanumsort(o, t, opts.sortkeys) end
+      local sparse = sparse and #o > maxn -- disable sparsness if only numeric keys (shorter output)
+      for n, key in ipairs(o) do
+        local value, ktype, plainindex = t[key], type(key), n <= maxn and not sparse
+        if opts.valignore and opts.valignore[value] -- skip ignored values; do nothing
+        or opts.keyallow and not opts.keyallow[key]
+        or opts.valtypeignore and opts.valtypeignore[type(value)] -- skipping ignored value types
+        or sparse and value == nil then -- skipping nils; do nothing
+        elseif ktype == 'table' or ktype == 'function' or badtype[ktype] then
+          if not seen[key] and not globals[key] then
+            sref[#sref+1] = 'placeholder'
+            local sname = safename(iname, gensym(key)) -- iname is table for local variables
+            sref[#sref] = val2str(key,sname,indent,sname,iname,true) end
+          sref[#sref+1] = 'placeholder'
+          local path = seen[t]..'['..(seen[key] or globals[key] or gensym(key))..']'
+          sref[#sref] = path..space..'='..space..(seen[value] or val2str(value,nil,indent,path))
+        else
+          out[#out+1] = val2str(value,key,indent,insref,seen[t],plainindex,level+1)
+        end
+      end
+      local prefix = string.rep(indent or '', level)
+      local head = indent and '{\n'..prefix..indent or '{'
+      local body = table.concat(out, ','..(indent and '\n'..prefix..indent or space))
+      local tail = indent and "\n"..prefix..'}' or '}'
+      return (custom and custom(tag,head,body,tail) or tag..head..body..tail)..comment(t, level)
+    elseif badtype[ttype] then
+      seen[t] = insref or spath
+      return tag..globerr(t, level)
+    elseif ttype == 'function' then
+      seen[t] = insref or spath
+      local ok, res = pcall(string.dump, t)
+      local func = ok and ((opts.nocode and "function() --[[..skipped..]] end" or
+        "((loadstring or load)("..safestr(res)..",'@serialized'))")..comment(t, level))
+      return tag..(func or globerr(t, level))
+    else return tag..safestr(t) end -- handle all other types
+  end
+  local sepr = indent and "\n" or ";"..space
+  local body = val2str(t, name, indent) -- this call also populates sref
+  local tail = #sref>1 and table.concat(sref, sepr)..sepr or ''
+  local warn = opts.comment and #sref>1 and space.."--[[incomplete output with shared/self-references skipped]]" or ''
+  return not name and body..warn or "do local "..body..sepr..tail.."return "..name..sepr.."end"
+end
+
+local function deserialize(data, opts)
+  local f, res = (loadstring or load)('return '..data)
+  if not f then f, res = (loadstring or load)(data) end
+  if not f then return f, res end
+  if opts and opts.safe == false then return pcall(f) end
+
+  local count, thread = 0, coroutine.running()
+  local h, m, c = debug.gethook(thread)
+  debug.sethook(function (e, l) count = count + 1
+    if count >= 3 then error("cannot call functions") end
+  end, "c")
+  local res = {pcall(f)}
+  count = 0 -- set again, otherwise it's tripped on the next sethook
+  debug.sethook(thread, h, m, c)
+  return (table.unpack or unpack)(res)
+end
+
+local function merge(a, b) if b then for k,v in pairs(b) do a[k] = v end end; return a; end
+return { _NAME = n, _COPYRIGHT = c, _DESCRIPTION = d, _VERSION = v, serialize = s,
+  load = deserialize,
+  dump = function(a, opts) return s(a, merge({name = '_', compact = true, sparse = true}, opts)) end,
+  line = function(a, opts) return s(a, merge({sortkeys = true, comment = true}, opts)) end,
+  block = function(a, opts) return s(a, merge({indent = '  ', sortkeys = true, comment = true}, opts)) end }
diff --git a/plugins/location.lua b/plugins/location.lua
index 7990a15..f6d876b 100644
--- a/plugins/location.lua
+++ b/plugins/location.lua
@@ -7,7 +7,9 @@
 
 -- Globals
 -- If you have a google api key for the geocoding/timezone api
-api_key = config.google_api_key or nil
+
+api_key = nil
+
 base_api = "https://maps.googleapis.com/maps/api"
 
 function delay_s(delay)
diff --git a/plugins/plugins.lua b/plugins/plugins.lua
index 3525ee9..d656824 100644
--- a/plugins/plugins.lua
+++ b/plugins/plugins.lua
@@ -6,7 +6,7 @@ function enable_plugin( filename )
 	-- Checks if plugin exists
 	if plugin_exists(filename) then
 		-- Add to the config table
-		table.insert(config.enabled_plugins, filename)
+		table.insert(_config.enabled_plugins, filename)
 		-- Reload the plugins
 		return reload_plugins( )
 	else
@@ -14,18 +14,18 @@ function enable_plugin( filename )
 	end
 end
 
-function disable_plugin( filename )
+function disable_plugin( name )
 	-- Check if plugins exists
-	if not plugin_exists(filename) then
-		return 'Plugin '..filename..' does not exists'
+	if not plugin_exists(name) then
+		return 'Plugin '..name..' does not exists'
 	end
-	local k = plugin_enabled(filename)
+	local k = plugin_enabled(name)
 	-- Check if plugin is enabled
 	if not k then
-		return 'Plugin '..filename..' not enabled'
+		return 'Plugin '..name..' not enabled'
 	end
 	-- Disable and reload
-	table.remove(config.enabled_plugins, k)
+	table.remove(_config.enabled_plugins, k)
 	return reload_plugins(true)		
 end
 
@@ -37,7 +37,7 @@ end
 
 -- Retruns the key (index) in the config.enabled_plugins table
 function plugin_enabled( name )
-	for k,v in pairs(config.enabled_plugins) do
+	for k,v in pairs(_config.enabled_plugins) do
 		if name == v then
 			return k
 		end
@@ -49,7 +49,7 @@ end
 -- Returns true if file exists in plugins folder
 function plugin_exists( name )
   for k,v in pairs(plugins_names()) do
-    if name == v then
+    if name..'.lua' == v then
       return true
     end
   end
@@ -62,13 +62,13 @@ function list_plugins(only_enabled)
 		--  ✔ enabled, ❌ disabled
 		local status = '❌'
 		-- Check if is enabled
-		for k2, v2 in pairs(config.enabled_plugins) do
-			if v == v2 then 
+		for k2, v2 in pairs(_config.enabled_plugins) do
+			if v == v2..'.lua' then 
 				status = '✔' 
 			end
 		end
 		if not only_enabled or status == '✔' then
-			text = text..v..' '..status..'\n'
+			text = text..v..'\b\t'..status..'\n'
 		end
 	end
 	return text
@@ -97,7 +97,7 @@ end
 
 return {
 	description = "Enables, disables and reloads plugins", 
-	usage = "!plugins, !plugins enable [plugin.lua], !plugins disable [plugin.lua], !plugins reload",
+	usage = "!plugins, !plugins enable [plugin], !plugins disable [plugin], !plugins reload",
 	patterns = {
 		"^!plugins$",
 		"^!plugins? (enable) (.*)$",
diff --git a/plugins/time.lua b/plugins/time.lua
index 462a2e2..0bf0ed9 100644
--- a/plugins/time.lua
+++ b/plugins/time.lua
@@ -5,7 +5,8 @@
 
 -- Globals
 -- If you have a google api key for the geocoding/timezone api
-api_key  = config.google_api_key or nil
+api_key  = nil
+
 base_api = "https://maps.googleapis.com/maps/api"
 dateFormat = "%A %d %B - %H:%M:%S"
 
diff --git a/plugins/twitter.lua b/plugins/twitter.lua
index e1cc3b0..312e746 100644
--- a/plugins/twitter.lua
+++ b/plugins/twitter.lua
@@ -1,9 +1,9 @@
 local OAuth = require "OAuth"
 
-local consumer_key = config.twitter.consumer_key
-local consumer_secret = config.twitter.consumer_secret
-local access_token = config.twitter.access_token
-local access_token_secret = config.twitter.access_token_secret
+local consumer_key = ""
+local consumer_secret = ""
+local access_token = ""
+local access_token_secret = ""
 
 local client = OAuth.new(consumer_key, consumer_secret, {
     RequestToken = "https://api.twitter.com/oauth/request_token", 
diff --git a/plugins/twitter_send.lua b/plugins/twitter_send.lua
index d982247..e995e25 100644
--- a/plugins/twitter_send.lua
+++ b/plugins/twitter_send.lua
@@ -1,9 +1,9 @@
 local OAuth = require "OAuth"
 
-local consumer_key = config.twitter.consumer_key
-local consumer_secret = config.twitter.consumer_secret
-local access_token = config.twitter.access_token
-local access_token_secret = config.twitter.access_token_secret
+local consumer_key = ""
+local consumer_secret = ""
+local access_token = ""
+local access_token_secret = ""
 
 local client = OAuth.new(consumer_key, consumer_secret, {
     RequestToken = "https://api.twitter.com/oauth/request_token", 

From 4774bf52e8206dbc9d2776c9c882b91bd086f477 Mon Sep 17 00:00:00 2001
From: yago <yagoperezs@gmail.com>
Date: Wed, 31 Dec 2014 17:45:52 +0100
Subject: [PATCH 06/17] Multiple plugins in one message

---
 bot/bot.lua       | 54 +++++---------------------------------
 plugins/stats.lua | 67 ++++++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 70 insertions(+), 51 deletions(-)

diff --git a/bot/bot.lua b/bot/bot.lua
index 226c084..08242b4 100644
--- a/bot/bot.lua
+++ b/bot/bot.lua
@@ -5,7 +5,7 @@ json = (loadfile "./libs/JSON.lua")()
 serpent = (loadfile "./libs/serpent.lua")()
 require("./bot/utils")
 
-VERSION = '0.8.0'
+VERSION = '0.8.1'
 
 function on_msg_receive (msg)
   vardump(msg)
@@ -14,7 +14,6 @@ function on_msg_receive (msg)
     return
   end
 
-  update_user_stats(msg)
   do_action(msg)
 
   mark_read(get_receiver(msg), ok_cb, false)
@@ -30,7 +29,6 @@ function on_binlog_replay_end ()
   -- See plugins/ping.lua as an example for cron
 
   _config = load_config()
-  _users = load_user_stats()
 
   -- load plugins
   plugins = {}
@@ -63,16 +61,15 @@ function do_action(msg)
   for name, desc in pairs(plugins) do
     -- print("Trying module", name)
     for k, pattern in pairs(desc.patterns) do
-      -- print("Trying", text, "against", pattern)
+      print("Trying", text, "against", pattern)
       matches = { string.match(text, pattern) }
       if matches[1] then
-        print("  matches",pattern)
+        print("  matches", pattern)
         if desc.run ~= nil then
           result = desc.run(msg, matches)
-          print("  sending", result)
           if (result) then
+            print("  sending", result)
             _send_msg(receiver, result)
-            return
           end
         end
       end
@@ -105,6 +102,7 @@ function save_config( )
   })
   file:write(serialized)
   file:close()
+  print ('saved config into ./bot/config.lua')
 end
 
 
@@ -150,47 +148,7 @@ function create_config( )
   })
   file:write(serialized)
   file:close()
-end
-
-function update_user_stats(msg)
-   -- Save user to _users table
-  local from_id = tostring(msg.from.id)
-  local to_id = tostring(msg.to.id)
-  local user_name = get_name(msg)
-  -- If last name is nil dont save last_name.
-  local user_last_name = msg.from.last_name
-  local user_print_name = msg.from.print_name
-  if _users[to_id] == nil then
-    _users[to_id] = {}
-  end
-  if _users[to_id][from_id] == nil then
-    _users[to_id][from_id] = {
-      name = user_name,
-      last_name = user_last_name,
-      print_name = user_print_name,
-      msg_num = 1
-    }
-  else
-    local actual_num = _users[to_id][from_id].msg_num
-    _users[to_id][from_id].msg_num = actual_num + 1
-    -- And update last_name
-    _users[to_id][from_id].last_name = user_last_name
-  end
-end
-
-function load_user_stats()
-  local f = io.open('res/users.json', "r+")
-  -- If file doesn't exists
-  if f == nil then
-    f = io.open('res/users.json', "w+")
-    f:write("{}") -- Write empty table
-    f:close()
-    return {}
-  else
-    local c = f:read "*a"
-    f:close()
-    return json:decode(c)
-  end
+  print ('saved config into ./bot/config.lua')
 end
 
 function on_our_id (id)
diff --git a/plugins/stats.lua b/plugins/stats.lua
index 2986ddb..12800b4 100644
--- a/plugins/stats.lua
+++ b/plugins/stats.lua
@@ -1,12 +1,56 @@
-function run(msg, matches)
-	vardump(_users)
+function update_user_stats(msg)
+  -- Save user to _users table
+  local from_id = tostring(msg.from.id)
+  local to_id = tostring(msg.to.id)
+  local user_name = get_name(msg)
+  print ('New message from '..user_name..'['..to_id..']'..'['..from_id..']')
+  -- If last name is nil dont save last_name.
+  local user_last_name = msg.from.last_name
+  local user_print_name = msg.from.print_name
+  if _users[to_id] == nil then
+    _users[to_id] = {}
+  end
+  if _users[to_id][from_id] == nil then
+    _users[to_id][from_id] = {
+      name = user_name,
+      last_name = user_last_name,
+      print_name = user_print_name,
+      msg_num = 1
+    }
+  else
+    local actual_num = _users[to_id][from_id].msg_num
+    _users[to_id][from_id].msg_num = actual_num + 1
+    -- And update last_name
+    _users[to_id][from_id].last_name = user_last_name
+  end
+end
+
+function load_user_stats()
+  local f = io.open('res/users.json', "r+")
+  -- If file doesn't exists
+  if f == nil then
+    f = io.open('res/users.json', "w+")
+    f:write("{}") -- Write empty table
+    f:close()
+    return {}
+  else
+    local c = f:read "*a"
+    f:close()
+    return json:decode(c)
+  end
+end
+
+function save_stats()
 	-- Save stats to file
 	local json_users = json:encode_pretty(_users)
 	vardump(json_users)
 	file_users = io.open ("./res/users.json", "w")
 	file_users:write(json_users)
 	file_users:close()
+end
 
+function get_stats_status( msg )
+	-- vardump(_users)
 	local text = ""
   	local to_id = tostring(msg.to.id)
 
@@ -17,12 +61,29 @@ function run(msg, matches)
 			text = text..user.name.." "..user.last_name.." ["..id.."]: "..user.msg_num.."\n"
 		end
 	end
+	print("usuarios: "..text)
 	return text
 end
 
+function run(msg, matches)
+	-- TODO: I need to know wich patterns matches.
+	if matches[1] == "!stats" then
+		return get_stats_status(msg)
+	else 
+		print ("update stats")
+		update_user_stats(msg)
+	end
+end
+
+-- TODO: local vars
+_users = load_user_stats()
+
 return {
     description = "Numer of messages by user", 
     usage = "!stats",
-    patterns = {"^!stats"}, 
+    patterns = {
+        ".*",
+    	"^!stats"
+    	}, 
     run = run 
 }
\ No newline at end of file

From ae0411ff258b7e766348d827f72b74568128c164 Mon Sep 17 00:00:00 2001
From: yago <yagoperezs@gmail.com>
Date: Thu, 1 Jan 2015 13:54:43 +0100
Subject: [PATCH 07/17] save config on plugin enabled / disabled

---
 bot/bot.lua         | 2 +-
 plugins/plugins.lua | 2 ++
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/bot/bot.lua b/bot/bot.lua
index 0f39ac2..7cdefc7 100644
--- a/bot/bot.lua
+++ b/bot/bot.lua
@@ -83,7 +83,7 @@ function do_action(msg)
   for name, desc in pairs(plugins) do
     -- print("Trying module", name)
     for k, pattern in pairs(desc.patterns) do
-      print("Trying", text, "against", pattern)
+      -- print("Trying", text, "against", pattern)
       matches = { string.match(text, pattern) }
       if matches[1] then
         print("  matches",pattern)
diff --git a/plugins/plugins.lua b/plugins/plugins.lua
index d656824..157f434 100644
--- a/plugins/plugins.lua
+++ b/plugins/plugins.lua
@@ -7,6 +7,7 @@ function enable_plugin( filename )
 	if plugin_exists(filename) then
 		-- Add to the config table
 		table.insert(_config.enabled_plugins, filename)
+		save_config()
 		-- Reload the plugins
 		return reload_plugins( )
 	else
@@ -26,6 +27,7 @@ function disable_plugin( name )
 	end
 	-- Disable and reload
 	table.remove(_config.enabled_plugins, k)
+	save_config( )
 	return reload_plugins(true)		
 end
 

From 7ce798b21b596a8cafaaabd855f6c64bac0037e5 Mon Sep 17 00:00:00 2001
From: yago <yagoperezs@gmail.com>
Date: Thu, 1 Jan 2015 13:55:14 +0100
Subject: [PATCH 08/17] invite plugin

---
 launch.sh          |  2 +-
 plugins/invite.lua | 42 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 43 insertions(+), 1 deletion(-)
 create mode 100644 plugins/invite.lua

diff --git a/launch.sh b/launch.sh
index 1057d6b..56ce55d 100755
--- a/launch.sh
+++ b/launch.sh
@@ -14,4 +14,4 @@ if [ ! -f ./tg/bin/telegram-cli ]; then
     exit
 fi
 
-./tg/bin/telegram-cli -k tg/tg-server.pub -s ./bot/bot.lua
+./tg/bin/telegram-cli -k tg/tg-server.pub -s ./bot/bot.lua -W
diff --git a/plugins/invite.lua b/plugins/invite.lua
new file mode 100644
index 0000000..b47e848
--- /dev/null
+++ b/plugins/invite.lua
@@ -0,0 +1,42 @@
+-- Invite other user to the chat group.
+-- Use !invite name User_name or !invite id id_number
+-- The User_name is the print_name (there are no spaces but _)
+
+do
+
+local function run(msg, matches)
+	-- User submitted a user name
+	if matches[1] == "name" then
+		user = matches[2]
+		user = string.gsub(user," ","_")
+	end
+	-- User submitted an id
+	if matches[1] == "id" then
+		user = matches[2]
+		user = 'user#id'..user
+	end
+	-- The message must come from a chat group
+	if msg.to.type == 'chat' then
+    	chat = 'chat#id'..msg.to.id
+  	else 
+  		return 'This isnt a chat group!'
+  	end
+  	print ("Trying to add: "..user.." to "..chat)
+	status = chat_add_user (chat, user, ok_cb, false)
+	if not status then
+		return "An error happened"
+	end
+	return "Added user: "..user.." to "..chat
+end
+
+return {
+    description = "Invite other user to the chat group", 
+    usage = "!invite name [user_name], !invite id [user_id]",
+    patterns = {
+      "^!invite (name) (.*)$",
+      "^!invite (id) (%d+)$"
+    }, 
+    run = run 
+}
+
+end
\ No newline at end of file

From a889d410f146d8ec40f58f5a391e61a75da7ba9b Mon Sep 17 00:00:00 2001
From: yago <yagoperezs@gmail.com>
Date: Thu, 1 Jan 2015 16:06:24 +0100
Subject: [PATCH 09/17] serialize config with function

---
 bot/bot.lua   | 16 ++--------------
 bot/utils.lua | 11 +++++++++++
 launch.sh     |  2 +-
 3 files changed, 14 insertions(+), 15 deletions(-)

diff --git a/bot/bot.lua b/bot/bot.lua
index 7cdefc7..36171fc 100644
--- a/bot/bot.lua
+++ b/bot/bot.lua
@@ -121,13 +121,7 @@ end
 
 -- Save the content of _config to config.lua
 function save_config( )
-  file = io.open('./bot/config.lua', 'w+')
-  local serialized = serpent.block(_config, {
-    comment = false,
-    name = "config"
-  })
-  file:write(serialized)
-  file:close()
+  serialize_to_file(_config, './bot/config.lua')
   print ('saved config into ./bot/config.lua')
 end
 
@@ -167,13 +161,7 @@ function create_config( )
       "youtube" },
     sudo_users = {our_id}  
   }
-  file = io.open('./bot/config.lua', 'w+')
-  local serialized = serpent.block(config, {
-    comment = false,
-    name = "config"
-  })
-  file:write(serialized)
-  file:close()
+  serialize_to_file(config, './bot/config.lua')
   print ('saved config into ./bot/config.lua')
 end
 
diff --git a/bot/utils.lua b/bot/utils.lua
index 81079e4..bce3c4a 100644
--- a/bot/utils.lua
+++ b/bot/utils.lua
@@ -169,4 +169,15 @@ function file_exists(name)
   else 
     return false 
   end
+end
+
+-- Save into file the data serialized for lua.
+function serialize_to_file(data, file)
+  file = io.open(file, 'w+')
+  local serialized = serpent.block(data, {
+    comment = false,
+    name = "_"
+  })
+  file:write(serialized)
+  file:close()
 end
\ No newline at end of file
diff --git a/launch.sh b/launch.sh
index 56ce55d..09399ab 100755
--- a/launch.sh
+++ b/launch.sh
@@ -14,4 +14,4 @@ if [ ! -f ./tg/bin/telegram-cli ]; then
     exit
 fi
 
-./tg/bin/telegram-cli -k tg/tg-server.pub -s ./bot/bot.lua -W
+./tg/bin/telegram-cli -k tg/tg-server.pub -s ./bot/bot.lua -W -l 1

From e22d2670fdbc81a59b78329174ac28557e5062b0 Mon Sep 17 00:00:00 2001
From: yago <yagoperezs@gmail.com>
Date: Thu, 1 Jan 2015 16:06:43 +0100
Subject: [PATCH 10/17] stats with local scope

---
 plugins/stats.lua | 81 +++++++++++++++++++++++++++--------------------
 1 file changed, 46 insertions(+), 35 deletions(-)

diff --git a/plugins/stats.lua b/plugins/stats.lua
index 12800b4..320f8c5 100644
--- a/plugins/stats.lua
+++ b/plugins/stats.lua
@@ -1,5 +1,14 @@
+-- Saves the number of messages from a user
+-- Can check the number of messages with !stats 
+
+do
+
+local socket = require('socket') 
+local _file_stats = './res/stats.lua'
+local _stats
+
 function update_user_stats(msg)
-  -- Save user to _users table
+  -- Save user to stats table
   local from_id = tostring(msg.from.id)
   local to_id = tostring(msg.to.id)
   local user_name = get_name(msg)
@@ -7,54 +16,53 @@ function update_user_stats(msg)
   -- If last name is nil dont save last_name.
   local user_last_name = msg.from.last_name
   local user_print_name = msg.from.print_name
-  if _users[to_id] == nil then
-    _users[to_id] = {}
+  if _stats[to_id] == nil then
+    print ('New stats key to_id: '..to_id)
+    _stats[to_id] = {}
   end
-  if _users[to_id][from_id] == nil then
-    _users[to_id][from_id] = {
+  if _stats[to_id][from_id] == nil then
+    print ('New stats key from_id: '..to_id)
+    _stats[to_id][from_id] = {
       name = user_name,
       last_name = user_last_name,
       print_name = user_print_name,
       msg_num = 1
     }
   else
-    local actual_num = _users[to_id][from_id].msg_num
-    _users[to_id][from_id].msg_num = actual_num + 1
+    print ('Updated '..to_id..' '..from_id)
+    local actual_num = _stats[to_id][from_id].msg_num
+    _stats[to_id][from_id].msg_num = actual_num + 1
     -- And update last_name
-    _users[to_id][from_id].last_name = user_last_name
+    _stats[to_id][from_id].last_name = user_last_name
   end
 end
 
-function load_user_stats()
-  local f = io.open('res/users.json', "r+")
+function read_file_stats( )
+  local f = io.open(_file_stats, "r+")
   -- If file doesn't exists
   if f == nil then
-    f = io.open('res/users.json', "w+")
-    f:write("{}") -- Write empty table
-    f:close()
-    return {}
+    -- Create a new empty table
+    print ('Created user stats file '.._file_stats)
+    serialize_to_file({}, _file_stats)
   else
-    local c = f:read "*a"
-    f:close()
-    return json:decode(c)
+    print ('Stats loaded: '.._file_stats)
+    f:close() 
   end
+  return loadfile (_file_stats)()
 end
 
-function save_stats()
+
+local function save_stats()
 	-- Save stats to file
-	local json_users = json:encode_pretty(_users)
-	vardump(json_users)
-	file_users = io.open ("./res/users.json", "w")
-	file_users:write(json_users)
-	file_users:close()
+	serialize_to_file(_stats, _file_stats)
 end
 
-function get_stats_status( msg )
-	-- vardump(_users)
+local function get_stats_status( msg )
+	-- vardump(stats)
 	local text = ""
   	local to_id = tostring(msg.to.id)
 
-	for id, user in pairs(_users[to_id]) do
+	for id, user in pairs(_stats[to_id]) do
 		if user.last_name == nil then
 			text = text..user.name.." ["..id.."]: "..user.msg_num.."\n"
 		else
@@ -65,25 +73,28 @@ function get_stats_status( msg )
 	return text
 end
 
-function run(msg, matches)
-	-- TODO: I need to know wich patterns matches.
-	if matches[1] == "!stats" then
-		return get_stats_status(msg)
+local function run(msg, matches)
+	if matches[1] == "stats" then -- Hack
+    return get_stats_status(msg)
 	else 
 		print ("update stats")
 		update_user_stats(msg)
+    print(socket.gettime())
+    save_stats()
+    print(socket.gettime())
 	end
 end
 
--- TODO: local vars
-_users = load_user_stats()
+_stats = read_file_stats()
 
 return {
     description = "Numer of messages by user", 
     usage = "!stats",
     patterns = {
-        ".*",
-    	"^!stats"
+      ".*",
+    	"^!(stats)"
     	}, 
     run = run 
-}
\ No newline at end of file
+}
+
+end
\ No newline at end of file

From 68fed25b495b77bc50313b6471fa93301c116c84 Mon Sep 17 00:00:00 2001
From: yago <yagoperezs@gmail.com>
Date: Sun, 4 Jan 2015 14:22:28 +0100
Subject: [PATCH 11/17] can define if command is for privileged users only

---
 bot/bot.lua         | 22 ++++++++++++++--------
 plugins/plugins.lua |  3 ++-
 plugins/stats.lua   |  4 +---
 3 files changed, 17 insertions(+), 12 deletions(-)

diff --git a/bot/bot.lua b/bot/bot.lua
index 36171fc..a0deba0 100644
--- a/bot/bot.lua
+++ b/bot/bot.lua
@@ -86,16 +86,22 @@ function do_action(msg)
       -- print("Trying", text, "against", pattern)
       matches = { string.match(text, pattern) }
       if matches[1] then
-        print("  matches",pattern)
+        print("  matches", pattern)
         if desc.run ~= nil then
-          result = desc.run(msg, matches)
-          -- print("  sending", result)
-          if (result) then
-            local result2 = do_lex(msg, result)
-            if (result2 == nil) then 
-              result2 = result
+          -- If plugin is for privileged user
+          if desc.privileged and not is_sudo(msg) then
+            local text = 'This plugin requires privileged user'
+            send_msg(receiver, text, ok_cb, false)
+          else 
+            result = desc.run(msg, matches)
+            -- print("  sending", result)
+            if (result) then
+              local result2 = do_lex(msg, result)
+              if (result2 == nil) then 
+                result2 = result
+              end
+              _send_msg(receiver, result2)
             end
-            _send_msg(receiver, result2)
           end
         end
       end
diff --git a/plugins/plugins.lua b/plugins/plugins.lua
index 157f434..63410f7 100644
--- a/plugins/plugins.lua
+++ b/plugins/plugins.lua
@@ -106,5 +106,6 @@ return {
 		"^!plugins? (disable) (.*)$",
 		"^!plugins? (reload)$"
 		}, 
-	run = run 
+	run = run,
+	privileged = true
 }
\ No newline at end of file
diff --git a/plugins/stats.lua b/plugins/stats.lua
index 320f8c5..70ce32e 100644
--- a/plugins/stats.lua
+++ b/plugins/stats.lua
@@ -12,7 +12,7 @@ function update_user_stats(msg)
   local from_id = tostring(msg.from.id)
   local to_id = tostring(msg.to.id)
   local user_name = get_name(msg)
-  print ('New message from '..user_name..'['..to_id..']'..'['..from_id..']')
+  print ('New message from '..user_name..'['..from_id..']'..' to '..to_id)
   -- If last name is nil dont save last_name.
   local user_last_name = msg.from.last_name
   local user_print_name = msg.from.print_name
@@ -79,9 +79,7 @@ local function run(msg, matches)
 	else 
 		print ("update stats")
 		update_user_stats(msg)
-    print(socket.gettime())
     save_stats()
-    print(socket.gettime())
 	end
 end
 

From ffa72bb22f62e0ce3feaa99d61d377ab8b2dbb45 Mon Sep 17 00:00:00 2001
From: yago <yagoperezs@gmail.com>
Date: Sun, 4 Jan 2015 20:32:04 +0100
Subject: [PATCH 12/17] do_lex infinite loop when "mutated = false"

---
 bot/bot.lua | 26 +++++++++-----------------
 1 file changed, 9 insertions(+), 17 deletions(-)

diff --git a/bot/bot.lua b/bot/bot.lua
index a0deba0..20c139a 100644
--- a/bot/bot.lua
+++ b/bot/bot.lua
@@ -49,17 +49,12 @@ function msg_valid(msg)
 end
 
 function do_lex(msg, text)
-  local mutated = true
-  while mutated do
-    mutated = false
-    for name, desc in pairs(plugins) do
-      if (desc.lex ~= nil) then
-        result = desc.lex(msg, text)
-        if (result ~= nil) then
-          -- print ("Mutating to " .. result)
-          text = result
-          mutated = true
-        end
+  for name, desc in pairs(plugins) do
+    if (desc.lex ~= nil) then
+      result = desc.lex(msg, text)
+      if (result ~= nil) then
+        print ("Mutating to " .. result)
+        text = result
       end
     end
   end
@@ -78,7 +73,7 @@ function do_action(msg)
   end
   -- print("Received msg", text)
 
-  text = do_lex(msg, text)
+  msg.text = do_lex(msg, text)
 
   for name, desc in pairs(plugins) do
     -- print("Trying module", name)
@@ -96,11 +91,8 @@ function do_action(msg)
             result = desc.run(msg, matches)
             -- print("  sending", result)
             if (result) then
-              local result2 = do_lex(msg, result)
-              if (result2 == nil) then 
-                result2 = result
-              end
-              _send_msg(receiver, result2)
+              result = do_lex(msg, result)
+              _send_msg(receiver, result)
             end
           end
         end

From 08e5a6c85f2b3abf5d6d36d6d4069c8bb3b5484d Mon Sep 17 00:00:00 2001
From: yago <yagoperezs@gmail.com>
Date: Tue, 6 Jan 2015 14:10:26 +0100
Subject: [PATCH 13/17] Checks if Twitter keys are empty

---
 bot/utils.lua       |  5 +++++
 plugins/twitter.lua | 23 ++++++++++++++---------
 2 files changed, 19 insertions(+), 9 deletions(-)

diff --git a/bot/utils.lua b/bot/utils.lua
index bce3c4a..506727b 100644
--- a/bot/utils.lua
+++ b/bot/utils.lua
@@ -180,4 +180,9 @@ function serialize_to_file(data, file)
   })
   file:write(serialized)
   file:close()
+end
+
+-- Retruns true if the string is empty
+function string:isempty()
+  return self == nil or self == ''
 end
\ No newline at end of file
diff --git a/plugins/twitter.lua b/plugins/twitter.lua
index 312e746..725873a 100644
--- a/plugins/twitter.lua
+++ b/plugins/twitter.lua
@@ -16,16 +16,23 @@ local client = OAuth.new(consumer_key, consumer_secret, {
 
 function run(msg, matches)
 
+  if consumer_key:isempty() then
+    return "Twitter Consumer Key is empty, write it in plugins/twitter.lua"
+  end
+  if consumer_secret:isempty() then
+    return "Twitter Consumer Secret is empty, write it in plugins/twitter.lua"
+  end
+  if access_token:isempty() then
+    return "Twitter Access Token is empty, write it in plugins/twitter.lua"
+  end
+  if access_token_secret:isempty() then
+    return "Twitter Access Token Secret is empty, write it in plugins/twitter.lua"
+  end
+
   local twitter_url = "https://api.twitter.com/1.1/statuses/show/" .. matches[1] .. ".json"
-
-  print(twitter_url)
-
   local response_code, response_headers, response_status_line, response_body = client:PerformRequest("GET", twitter_url)
-  print(response_body)
   local response = json:decode(response_body)
 
-  print("response = ", response)
-
   local header = "Tweet from " .. response.user.name .. " (@" .. response.user.screen_name .. ")\n"
   local text = response.text
   
@@ -65,6 +72,4 @@ return {
     usage = "",
     patterns = {"https://twitter.com/[^/]+/status/([0-9]+)"}, 
     run = run 
-}
-
-
+}
\ No newline at end of file

From 7c8c56655fa902d4a8e7470212b6317fa3cdbe93 Mon Sep 17 00:00:00 2001
From: yago <yagoperezs@gmail.com>
Date: Tue, 6 Jan 2015 14:32:42 +0100
Subject: [PATCH 14/17] Checks if Twitter keys are empty

---
 plugins/twitter_send.lua | 26 ++++++++++++++++++++------
 1 file changed, 20 insertions(+), 6 deletions(-)

diff --git a/plugins/twitter_send.lua b/plugins/twitter_send.lua
index e995e25..cf243f7 100644
--- a/plugins/twitter_send.lua
+++ b/plugins/twitter_send.lua
@@ -9,23 +9,37 @@ local client = OAuth.new(consumer_key, consumer_secret, {
     RequestToken = "https://api.twitter.com/oauth/request_token", 
     AuthorizeUser = {"https://api.twitter.com/oauth/authorize", method = "GET"},
     AccessToken = "https://api.twitter.com/oauth/access_token"
-}, {
+    }, {
     OAuthToken = access_token,
     OAuthTokenSecret = access_token_secret
 })
 
 function run(msg, matches)
+    if consumer_key:isempty() then
+        return "Twitter Consumer Key is empty, write it in plugins/twitter.lua"
+    end
+    if consumer_secret:isempty() then
+        return "Twitter Consumer Secret is empty, write it in plugins/twitter.lua"
+    end
+    if access_token:isempty() then
+        return "Twitter Access Token is empty, write it in plugins/twitter.lua"
+    end
+    if access_token_secret:isempty() then
+        return "Twitter Access Token Secret is empty, write it in plugins/twitter.lua"
+    end
+
     if not is_sudo(msg) then
         return "You aren't allowed to send tweets"
     end
-	local response_code, response_headers, response_status_line, response_body = 
+    
+    local response_code, response_headers, response_status_line, response_body = 
     client:PerformRequest("POST", "https://api.twitter.com/1.1/statuses/update.json", {
-    	status = matches[1]
-    })
+        status = matches[1]
+        })
     if response_code ~= 200 then
-    	return "Error: "..response_code
+        return "Error: "..response_code
     end
-	return "Tweet sended"
+    return "Tweet sended"
 end
 
 return {

From 859fb485fa8682a1ec13dd0861b50356ad302778 Mon Sep 17 00:00:00 2001
From: yago <yagoperezs@gmail.com>
Date: Tue, 6 Jan 2015 14:51:38 +0100
Subject: [PATCH 15/17] show only plugin name

---
 plugins/plugins.lua | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/plugins/plugins.lua b/plugins/plugins.lua
index 63410f7..af729a7 100644
--- a/plugins/plugins.lua
+++ b/plugins/plugins.lua
@@ -70,7 +70,9 @@ function list_plugins(only_enabled)
 			end
 		end
 		if not only_enabled or status == '✔' then
-			text = text..v..'\b\t'..status..'\n'
+			-- get the name
+			v = string.match (v, "(.*)%.lua")
+			text = text..v..'  '..status..'\n'
 		end
 	end
 	return text

From 6c9efc902c475866f11c475246d74f274706029c Mon Sep 17 00:00:00 2001
From: yago <yagoperezs@gmail.com>
Date: Tue, 6 Jan 2015 21:04:45 +0100
Subject: [PATCH 16/17] data is the new res folder

---
 .gitignore             |  1 +
 bot/bot.lua            |  2 +-
 {res => data}/.gitkeep |  0
 plugins/get.lua        | 26 ++++++++++++++++----------
 plugins/set.lua        |  8 ++++----
 plugins/stats.lua      |  2 +-
 6 files changed, 23 insertions(+), 16 deletions(-)
 rename {res => data}/.gitkeep (100%)

diff --git a/.gitignore b/.gitignore
index 76174e7..e150a82 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
 res/
+data/
 bot/config.lua
\ No newline at end of file
diff --git a/bot/bot.lua b/bot/bot.lua
index 20c139a..108af81 100644
--- a/bot/bot.lua
+++ b/bot/bot.lua
@@ -5,7 +5,7 @@ json = (loadfile "./libs/JSON.lua")()
 serpent = (loadfile "./libs/serpent.lua")()
 require("./bot/utils")
 
-VERSION = '0.8.1'
+VERSION = '0.8.2'
 
 function on_msg_receive (msg)
   vardump(msg)
diff --git a/res/.gitkeep b/data/.gitkeep
similarity index 100%
rename from res/.gitkeep
rename to data/.gitkeep
diff --git a/plugins/get.lua b/plugins/get.lua
index 7f4631a..c254dc5 100644
--- a/plugins/get.lua
+++ b/plugins/get.lua
@@ -1,15 +1,21 @@
-local f = io.open('./res/values.json', "r+")
-if f == nil then
-  f = io.open('./res/values.json', "w+")
-  f:write("{}") -- Write empty table
-  f:close()
-  _values = {}
-else
-  local c = f:read "*a"
-  f:close()
-  _values = json:decode(c)
+local _file_values = './data/values.lua'
+
+function read_file_values( )
+  local f = io.open(_file_values, "r+")
+  -- If file doesn't exists
+  if f == nil then
+    -- Create a new empty table
+    print ('Created value file '.._file_values)
+    serialize_to_file({}, _file_values)
+  else
+    print ('Stats loaded: '.._file_values)
+    f:close() 
+  end
+  return loadfile (_file_values)()
 end
 
+_values = read_file_values()
+
 function fetch_value(chat, value_name)
   if (_values[chat] == nil) then
     return nil
diff --git a/plugins/set.lua b/plugins/set.lua
index c706f98..d971b9c 100644
--- a/plugins/set.lua
+++ b/plugins/set.lua
@@ -1,3 +1,5 @@
+local _file_values = './data/values.lua'
+
 function save_value(chat, text )
 	var_name, var_value = string.match(text, "!set (%a+) (.+)")
 	if (var_name == nil or var_value == nil) then
@@ -8,10 +10,8 @@ function save_value(chat, text )
 	end
 	_values[chat][var_name] = var_value
 
-	local json_text = json:encode_pretty(_values) 
-	file = io.open ("./res/values.json", "w+")
-	file:write(json_text)
-	file:close()
+	-- Save values to file
+	serialize_to_file(_values, _file_values)
 	
 	return "Saved "..var_name.." = "..var_value
 end
diff --git a/plugins/stats.lua b/plugins/stats.lua
index 70ce32e..e9fcf7e 100644
--- a/plugins/stats.lua
+++ b/plugins/stats.lua
@@ -4,7 +4,7 @@
 do
 
 local socket = require('socket') 
-local _file_stats = './res/stats.lua'
+local _file_stats = './data/stats.lua'
 local _stats
 
 function update_user_stats(msg)

From a0a3c771f8d05c2dd3129454f890a38c81c9a835 Mon Sep 17 00:00:00 2001
From: yago <yagoperezs@gmail.com>
Date: Tue, 6 Jan 2015 21:08:18 +0100
Subject: [PATCH 17/17] config.lua inside data folder

---
 bot/bot.lua | 17 +++++++++--------
 1 file changed, 9 insertions(+), 8 deletions(-)

diff --git a/bot/bot.lua b/bot/bot.lua
index 108af81..eceabc2 100644
--- a/bot/bot.lua
+++ b/bot/bot.lua
@@ -119,20 +119,21 @@ end
 
 -- Save the content of _config to config.lua
 function save_config( )
-  serialize_to_file(_config, './bot/config.lua')
-  print ('saved config into ./bot/config.lua')
+  serialize_to_file(_config, './data/config.lua')
+  print ('saved config into ./data/config.lua')
 end
 
 
 function load_config( )
-  local f = io.open('./bot/config.lua', "r")
+  local f = io.open('./data/config.lua', "r")
   -- If config.lua doesnt exists
   if not f then
-    print ("Created new config file: bot/config.lua")
+    print ("Created new config file: data/config.lua")
     create_config()
+  else
+    f:close()
   end
-  f:close()
-  local config = loadfile ("./bot/config.lua")()
+  local config = loadfile ("./data/config.lua")()
   for v,user in pairs(config.sudo_users) do
     print("Allowed user: " .. user)
   end
@@ -159,8 +160,8 @@ function create_config( )
       "youtube" },
     sudo_users = {our_id}  
   }
-  serialize_to_file(config, './bot/config.lua')
-  print ('saved config into ./bot/config.lua')
+  serialize_to_file(config, './data/config.lua')
+  print ('saved config into ./data/config.lua')
 end
 
 function on_our_id (id)