diff --git a/README.md b/README.md index 1ac8714..eb9868e 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ but on Telegram. Bot uses an OOP-style of lua as [described on Wikipedia](https://is.gd/f0Vadk) +Also, bot automatically detects language installed in the client. + TODO: Rewrite core to C, [lua have C API](https://www.lua.org/manual/5.3/manual.html#4) and C is faster. diff --git a/config.lua b/config.lua index ca07dba..7d51506 100644 --- a/config.lua +++ b/config.lua @@ -3,12 +3,17 @@ return { owner = 935626920 , -- hehe cmds = { 'eval', - 'rub', 'ping', + 'rub', + 'start', }, events = { 'command', - 'message', 'ready', }, -} \ No newline at end of file + parts = { + 'locale', + 'db', + 'client', + }, +} diff --git a/etc/api/core.lua b/etc/api/core.lua index 2db4bba..73c73f4 100644 --- a/etc/api/core.lua +++ b/etc/api/core.lua @@ -30,20 +30,24 @@ local function receiveUpdate(self, update) if update then self:emit('update', update) end if update.message then - local txt = update.message.text - local cmd, to = tools.fetchCmd(txt) + local msg = update.message + local cmd, to = tools.fetchCmd(msg.text or '') if cmd and (not to or to == self.info.username) then + -- need /cmd@bot in groups + if (msg.chat.type == 'group' or msg.chat.type == 'supergroup') + and not to then return end + local args = {} - txt = txt:sub(#cmd + #(to or {}) + 3) - for s in txt:gmatch '%S+' do table.insert(args, s) end + txt = msg.text:sub(#cmd + #(to or {}) + 3) + for s in msg.text:gmatch '%S+' do table.insert(args, s) end - update.message.cmd = cmd - update.message.args = args + msg.cmd = cmd + msg.args = args - return self:emit('command', update.message) + return self:emit('command', msg) elseif cmd then return end - self:emit('message', update.message) + self:emit('message', msg) elseif update.edited_message then self:emit('messageEdit', update.edited_message) diff --git a/src/cmds/eval.lua b/src/cmds/eval.lua index ec342d4..e430daf 100644 --- a/src/cmds/eval.lua +++ b/src/cmds/eval.lua @@ -29,8 +29,6 @@ local env = { return { private = true, - args = '', - desc = 'evaluates code', run = function(C, msg, owner) local s = '' local t = { diff --git a/src/cmds/ping.lua b/src/cmds/ping.lua index cea65dd..926cef7 100644 --- a/src/cmds/ping.lua +++ b/src/cmds/ping.lua @@ -1,6 +1,5 @@ return { - desc = 'ping pong', run = function(C, msg) - C.api:send(msg, 'Pong! ' .. (os.time() - msg.date) .. 's') + C.api:send(msg, msg.loc.pat:format(os.time() - msg.date)) end } \ No newline at end of file diff --git a/src/cmds/rub.lua b/src/cmds/rub.lua index 113c74e..8c7b1c1 100644 --- a/src/cmds/rub.lua +++ b/src/cmds/rub.lua @@ -5,64 +5,56 @@ local rub = { url = 'https://api.factmaven.com/xml-to-json/?xml=' .. 'https://www.cbr.ru/scripts/XML_daily.asp', - fmt = function(v, fmt) - fmt = type(fmt) == 'string' and fmt or '%d %s = %f' - return fmt:format(v.Nominal, v.Name, v.Value:gsub(',', '.')) - end + + tools = require 'etc.api.tools', } -function rub:course(wants, fmt) - local resp, succ = (require 'etc.api.tools').requ(self.url) - if not succ then - return {}, '[ошибка]', {} - end +function rub:course(wants) + local resp, succ = self.tools.requ(self.url) + if not succ then return 'err' end resp = resp.ValCurs + table.insert(resp.Valute, { + ID = 'R01000', + NumCode = '001', + CharCode = 'RUB', + Nominal = 1, + Name = 'Российский рубль', + Value = '1' + }) wants = type(wants) == 'table' and wants or {} local r, founds = {}, {} + for i = 1, #resp.Valute do local v = resp.Valute[i] if table.find(wants, v.CharCode) then table.insert(founds, v.CharCode) - table.insert(r, self.fmt(v, fmt)) + table.insert(r, ('%d %s (%s) - %f ₽'):format(v.Nominal, v.Name, v.CharCode, v.Value:gsub(',', '.'))) end end - local i = table.find(wants, 'RUB') - if i then - table.insert(founds, 'RUB') - table.insert(r, i, self.fmt({ - Nominal = 1, - Name = 'Российский рубль', - Value = '1' - }, fmt) .. ' :D') - end - return r, resp.Date, founds end -function rub.msg(C, msg) - local wants = {'USD', 'EUR', table.unpack(msg.args)} - for i = 1, #wants do wants[i] = wants[i]:upper() end - - local v, d, f = rub:course(wants, '%d %s - %f ₽') - local nf = {} - - for i = 1, #wants do - if not table.find(f, wants[i]) then - table.insert(nf, wants[i]) - end - end - - local s = 'Курс на ' .. d .. ':\n' .. table.concat(v, '\n') - if #nf > 0 then s = s .. '\n\n' .. 'Не нашлось: ' .. table.concat(nf, ', ') end - - C.api:reply(msg, s .. '\nДанные от Центробанка России') -end - return { - args = '[valute]...', - desc = 'ruble course', - run = rub.msg + run = function(C, msg) + local wants = {'USD', 'EUR', table.unpack(msg.args)} + for i = 1, #wants do wants[i] = wants[i]:upper() end -- uppercase + + local v, d, f = rub:course(wants) + if v == 'error' then + return C.api:reply(msg, C.locale:get('error', 'req_err')) + end + + local nf = {} + for _, i in pairs(wants) do + if not table.find(f, i) then table.insert(nf, i) end + end + + local s = msg.loc.cur:format(d, table.concat(v, '\n')) + if #nf > 0 then s = s .. msg.loc.notf .. table.concat(nf, ',') end + + C.api:reply(msg, s .. msg.loc.prov) + end } diff --git a/src/cmds/start.lua b/src/cmds/start.lua new file mode 100644 index 0000000..efc2688 --- /dev/null +++ b/src/cmds/start.lua @@ -0,0 +1,6 @@ +return { + hide = true, + run = function(C, msg) + C.api:reply(msg, 'TODO!') + end +} \ No newline at end of file diff --git a/src/events/command.lua b/src/events/command.lua index 9bf9d0a..73e26a4 100644 --- a/src/events/command.lua +++ b/src/events/command.lua @@ -1,23 +1,25 @@ return function(C, api, msg) local cmd = C.cmds[msg.cmd] local owner = msg.from.id == C.config.owner + if cmd == nil then - api:send(msg, 'Invaid command provided.') + api:send(msg, C.locale:get('error', 'inv_cmd')) elseif type(cmd.run) ~= 'function' then - api:send(msg, 'Command cannot be executed.') + api:send(msg, C.locale:get('error', 'cmd_run')) elseif cmd.private and not owner then - api:send(msg, 'You can\'t execute private commands!') + api:send(msg, C.locale:get('error', 'adm_cmd')) else + msg.loc = C.locale:get('cmds', msg.cmd) local succ, err = pcall(cmd.run, C, msg, owner) if not succ then - api:reply(msg, 'Произошла ошибочка, которая была отправлена создателю') print(err) local cid = C.config.owner api:forward(cid, msg.chat.id, msg.message_id, false) api:send(cid, err) + api:reply(msg, msg.locale.error.not_suc) end end end \ No newline at end of file diff --git a/src/events/message.lua b/src/events/message.lua deleted file mode 100644 index a864740..0000000 --- a/src/events/message.lua +++ /dev/null @@ -1,3 +0,0 @@ -return function(C, api, msg) --- api:reply(msg, 'хай!') -end diff --git a/src/events/ready.lua b/src/events/ready.lua index 1af8c5a..8790990 100644 --- a/src/events/ready.lua +++ b/src/events/ready.lua @@ -29,10 +29,11 @@ return function(C, api) C:load 'cmds' local a = {} for k, v in pairs(C.cmds) do - if not v.private then + if not (v.private or v.hide) then + local cmd = C.locale:get('cmds', k) or {} table.insert(a, { command = k, - description = (v.args and v.args .. ' - ' or '') .. v.desc or 'no description' + description = (cmd.args and cmd.args .. ' - ' or '') .. (cmd.desc or C.locale:get('cmds', 'not_des')) }) end end diff --git a/src/locales/en.json b/src/locales/en.json new file mode 100644 index 0000000..55a04fc --- /dev/null +++ b/src/locales/en.json @@ -0,0 +1,31 @@ +{ + "error": { + "inv_cmd": "Invalid command provided.", + "adm_cmd": "You can't execute admin commands!", + "cmd_run": "This command cannot be executed now.", + "not_suc": "An error was occured which already sent to creator.", + "unk_err": "Unknown error.", + "req_err": "Request error." + }, + "cmds": { + "not_des": "no description", + + "eval": { + "args": "", + "desc": "executes code" + }, + "ping": { + "desc": "ping pong", + + "pat": "Pong! %ds" + }, + "rub": { + "args": "[valute]...", + "desc": "ruble course", + + "cur": "Currency at %s:\n%s", + "notf": "\nNot found: ", + "prov": "\nData provided from central bank of Russia." + } + } +} diff --git a/src/locales/ru.json b/src/locales/ru.json new file mode 100644 index 0000000..82fe87c --- /dev/null +++ b/src/locales/ru.json @@ -0,0 +1,31 @@ +{ + "error": { + "inv_cmd": "Указана неизвестная команда.", + "adm_cmd": "Вы не можете исполнять админские команды!", + "cmd_run": "Это команда не мжет быть выполнена сейчас.", + "not_suc": "Произошла ошибка, которая уже отправлена создателю.", + "unk_err": "Неизвестная ошибка.", + "req_err": "Ошибка запроса." + }, + "cmds": { + "not_des": "нет описания", + + "eval": { + "args": "<код>", + "desc": "исполняет код" + }, + "ping": { + "desc": "пинг-понг", + + "pat": "Понг! %d секунд" + }, + "rub": { + "args": "[валюта]...", + "desc": "курс рубля", + + "cur": "Курс на %s:\n%s", + "notf": "\nНе нашлось: ", + "prov": "\nДанные предоставлены центральным банком России." + } + } +} diff --git a/src/parts/core.lua b/src/parts/core.lua index 58938ba..b152c57 100644 --- a/src/parts/core.lua +++ b/src/parts/core.lua @@ -28,8 +28,11 @@ end function Core:ev(t, i, name, ...) local v = t[i] if v.name == name then - local succ = pcall(v.fn, self, ...) - if not succ then print('event "' .. name .. '" was failed') end + local succ, err = pcall(v.fn, self, ...) + if not succ then + print('event "' .. name .. '" was failed') + print(err) + end if v.type == 'once' then table.remove(t, i) end end end diff --git a/src/parts/locale.lua b/src/parts/locale.lua new file mode 100644 index 0000000..f5b2bc1 --- /dev/null +++ b/src/parts/locale.lua @@ -0,0 +1,33 @@ +local Locale = { + list = { + 'en', + 'ru' + }, + main = 'en', + + __newindex = function()end -- ro +} +Locale.__index = Locale + +function Locale:get(cat, k, lang) + assert(cat, 'Give category') + assert(k, 'Give key') + lang = lang or self.main + + local v = self[lang][cat][k] + if not v then + return self[self.main][cat][k] + else return v end +end + +return function(C) + local json = require 'etc.json' + + for i = 1, #Locale.list do + local n = Locale.list[i] + local f = io.open(('src/locales/%s.json'):format(n)) + Locale[n] = json.decode(f:read 'a') + end + + C.locale = setmetatable({}, Locale) +end