Merge pull request #45 from yagop/develop
New version 0.8.2! Changes about config and plugins.
This commit is contained in:
commit
0cc9ea0041
2
.gitignore
vendored
2
.gitignore
vendored
@ -1 +1,3 @@
|
||||
res/
|
||||
data/
|
||||
bot/config.lua
|
173
bot/bot.lua
173
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.2'
|
||||
|
||||
function on_msg_receive (msg)
|
||||
vardump(msg)
|
||||
@ -13,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)
|
||||
@ -22,15 +22,21 @@ end
|
||||
function ok_cb(extra, success, result)
|
||||
end
|
||||
|
||||
-- Callback to remove tmp files
|
||||
function rmtmp_cb(file_path, success, result)
|
||||
os.remove(file_path)
|
||||
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()
|
||||
|
||||
-- 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
|
||||
@ -42,6 +48,20 @@ function msg_valid(msg)
|
||||
end
|
||||
end
|
||||
|
||||
function do_lex(msg, text)
|
||||
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
|
||||
-- print("Text mutated to " .. text)
|
||||
return text
|
||||
end
|
||||
|
||||
-- Where magic happens
|
||||
function do_action(msg)
|
||||
local receiver = get_receiver(msg)
|
||||
@ -52,19 +72,28 @@ function do_action(msg)
|
||||
text = '['..msg.media.type..']'
|
||||
end
|
||||
-- print("Received msg", text)
|
||||
|
||||
msg.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)
|
||||
if (result) then
|
||||
_send_msg(receiver, result)
|
||||
return
|
||||
-- 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
|
||||
result = do_lex(msg, result)
|
||||
_send_msg(receiver, result)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -88,60 +117,51 @@ function _send_msg( destination, text)
|
||||
end
|
||||
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
|
||||
-- Save the content of _config to config.lua
|
||||
function save_config( )
|
||||
serialize_to_file(_config, './data/config.lua')
|
||||
print ('saved config into ./data/config.lua')
|
||||
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
|
||||
}
|
||||
|
||||
function load_config( )
|
||||
local f = io.open('./data/config.lua', "r")
|
||||
-- If config.lua doesnt exists
|
||||
if not f then
|
||||
print ("Created new config file: data/config.lua")
|
||||
create_config()
|
||||
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
|
||||
f:close()
|
||||
end
|
||||
local config = loadfile ("./data/config.lua")()
|
||||
for v,user in pairs(config.sudo_users) do
|
||||
print("Allowed user: " .. user)
|
||||
end
|
||||
return config
|
||||
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
|
||||
-- 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}
|
||||
}
|
||||
serialize_to_file(config, './data/config.lua')
|
||||
print ('saved config into ./data/config.lua')
|
||||
end
|
||||
|
||||
function on_our_id (id)
|
||||
@ -163,22 +183,12 @@ 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
|
||||
|
||||
-- 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..'.lua')()
|
||||
table.insert(plugins, t)
|
||||
end
|
||||
end
|
||||
|
||||
@ -199,10 +209,3 @@ end
|
||||
-- Start and load values
|
||||
our_id = 0
|
||||
now = os.time()
|
||||
|
||||
config = load_config()
|
||||
_users = load_user_stats()
|
||||
|
||||
-- load plugins
|
||||
plugins = {}
|
||||
load_plugins()
|
@ -1,13 +0,0 @@
|
||||
{
|
||||
"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": ""
|
||||
}
|
||||
}
|
@ -61,12 +61,18 @@ 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
|
||||
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 = ""
|
||||
@ -125,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
|
||||
@ -133,6 +139,7 @@ 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
|
||||
@ -140,3 +147,42 @@ function get_name(msg)
|
||||
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
|
||||
|
||||
-- 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
|
||||
|
||||
-- Retruns true if the string is empty
|
||||
function string:isempty()
|
||||
return self == nil or self == ''
|
||||
end
|
@ -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 -l 1
|
||||
|
@ -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.
|
||||
--
|
128
libs/serpent.lua
Normal file
128
libs/serpent.lua
Normal file
@ -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 }
|
@ -1,13 +1,30 @@
|
||||
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
|
||||
end
|
||||
if (value_name == nil ) then
|
||||
return nil
|
||||
end
|
||||
local value = _values[chat][value_name]
|
||||
return value
|
||||
end
|
||||
|
||||
function get_value(chat, value_name)
|
||||
@ -40,11 +57,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
|
||||
}
|
||||
|
||||
|
42
plugins/invite.lua
Normal file
42
plugins/invite.lua
Normal file
@ -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
|
@ -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)
|
||||
|
113
plugins/plugins.lua
Normal file
113
plugins/plugins.lua
Normal file
@ -0,0 +1,113 @@
|
||||
function enable_plugin( filename )
|
||||
-- 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)
|
||||
save_config()
|
||||
-- Reload the plugins
|
||||
return reload_plugins( )
|
||||
else
|
||||
return 'Plugin '..filename..' does not exists'
|
||||
end
|
||||
end
|
||||
|
||||
function disable_plugin( name )
|
||||
-- Check if plugins exists
|
||||
if not plugin_exists(name) then
|
||||
return 'Plugin '..name..' does not exists'
|
||||
end
|
||||
local k = plugin_enabled(name)
|
||||
-- Check if plugin is enabled
|
||||
if not k then
|
||||
return 'Plugin '..name..' not enabled'
|
||||
end
|
||||
-- Disable and reload
|
||||
table.remove(_config.enabled_plugins, k)
|
||||
save_config( )
|
||||
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..'.lua' == v then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
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..'.lua' then
|
||||
status = '✔'
|
||||
end
|
||||
end
|
||||
if not only_enabled or status == '✔' then
|
||||
-- get the name
|
||||
v = string.match (v, "(.*)%.lua")
|
||||
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
|
||||
-- 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 = "Enables, disables and reloads plugins",
|
||||
usage = "!plugins, !plugins enable [plugin], !plugins disable [plugin], !plugins reload",
|
||||
patterns = {
|
||||
"^!plugins$",
|
||||
"^!plugins? (enable) (.*)$",
|
||||
"^!plugins? (disable) (.*)$",
|
||||
"^!plugins? (reload)$"
|
||||
},
|
||||
run = run,
|
||||
privileged = true
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -1,28 +1,98 @@
|
||||
function run(msg, matches)
|
||||
vardump(_users)
|
||||
-- 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()
|
||||
-- Saves the number of messages from a user
|
||||
-- Can check the number of messages with !stats
|
||||
|
||||
do
|
||||
|
||||
local socket = require('socket')
|
||||
local _file_stats = './data/stats.lua'
|
||||
local _stats
|
||||
|
||||
function update_user_stats(msg)
|
||||
-- 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)
|
||||
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
|
||||
if _stats[to_id] == nil then
|
||||
print ('New stats key to_id: '..to_id)
|
||||
_stats[to_id] = {}
|
||||
end
|
||||
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
|
||||
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
|
||||
_stats[to_id][from_id].last_name = user_last_name
|
||||
end
|
||||
end
|
||||
|
||||
function read_file_stats( )
|
||||
local f = io.open(_file_stats, "r+")
|
||||
-- If file doesn't exists
|
||||
if f == nil then
|
||||
-- Create a new empty table
|
||||
print ('Created user stats file '.._file_stats)
|
||||
serialize_to_file({}, _file_stats)
|
||||
else
|
||||
print ('Stats loaded: '.._file_stats)
|
||||
f:close()
|
||||
end
|
||||
return loadfile (_file_stats)()
|
||||
end
|
||||
|
||||
|
||||
local function save_stats()
|
||||
-- Save stats to file
|
||||
serialize_to_file(_stats, _file_stats)
|
||||
end
|
||||
|
||||
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
|
||||
text = text..user.name.." "..user.last_name.." ["..id.."]: "..user.msg_num.."\n"
|
||||
end
|
||||
end
|
||||
print("usuarios: "..text)
|
||||
return text
|
||||
end
|
||||
|
||||
local function run(msg, matches)
|
||||
if matches[1] == "stats" then -- Hack
|
||||
return get_stats_status(msg)
|
||||
else
|
||||
print ("update stats")
|
||||
update_user_stats(msg)
|
||||
save_stats()
|
||||
end
|
||||
end
|
||||
|
||||
_stats = read_file_stats()
|
||||
|
||||
return {
|
||||
description = "Numer of messages by user",
|
||||
usage = "!stats",
|
||||
patterns = {"^!stats"},
|
||||
patterns = {
|
||||
".*",
|
||||
"^!(stats)"
|
||||
},
|
||||
run = run
|
||||
}
|
||||
|
||||
end
|
@ -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"
|
||||
|
||||
|
@ -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",
|
||||
@ -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
|
||||
|
||||
@ -66,5 +73,3 @@ return {
|
||||
patterns = {"https://twitter.com/[^/]+/status/([0-9]+)"},
|
||||
run = run
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,31 +1,45 @@
|
||||
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",
|
||||
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 {
|
||||
|
Reference in New Issue
Block a user