From 49fa684f47e84dd36302a2128c97889cbae2f242 Mon Sep 17 00:00:00 2001 From: Er2 Date: Mon, 19 Jul 2021 10:55:01 +0300 Subject: [PATCH] add readme, some code refactoring --- LICENSE | 2 +- README.md | 25 ++++++++++++++++ cmds/rub.lua | 4 +-- {tg => core}/api.lua | 22 ++++++++------ {tg => core}/core.lua | 64 ++++++++++++++++++++--------------------- core/events.lua | 39 +++++++++++++++++++++++++ core/init.lua | 1 + {tg => core}/json.lua | 0 {tg => core}/tools.lua | 41 ++++++++++++++------------ db/.gitignore | 4 +-- db/{db.lua => init.lua} | 0 init.lua | 39 ++++++++++++++++--------- tg/init.lua | 1 - 13 files changed, 162 insertions(+), 80 deletions(-) create mode 100644 README.md rename {tg => core}/api.lua (89%) rename {tg => core}/core.lua (58%) create mode 100644 core/events.lua create mode 100644 core/init.lua rename {tg => core}/json.lua (100%) rename {tg => core}/tools.lua (64%) rename db/{db.lua => init.lua} (100%) delete mode 100644 tg/init.lua diff --git a/LICENSE b/LICENSE index b83a141..e241737 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -zlib License +Zlib License Copyright (c) 2021 Er2 diff --git a/README.md b/README.md new file mode 100644 index 0000000..249c96c --- /dev/null +++ b/README.md @@ -0,0 +1,25 @@ +# Computer bot + +This bot is a reborn of [this](https://github.com/Er2pkg/computer) bot, +but on Telegram. + +Bot uses an OOP-style of lua +as [described on Wikipedia](https://is.gd/f0Vadk) + +TODO: Rewrite core to C, [lua have C API](https://www.lua.org/manual/5.3/manual.html#4) +and C is faster. + +# Installation + +[Alpine Linux](https://alpinelinux.org), root: + * Enable community repo (in wiki) + * Install: `apk add sudo git lua5.3 luarocks openssl-dev` + * Install dependencies: `luarocks-5.3 install luasec` + * Create user: `adduser user` + setup sudo and login to user + + * Get repo: `git clone https://github.com/Er2ch/comp-tg` + and `cd comp-tg` + * Change token and owner in `config.lua` + TODO: Use env instaed of config + * Run: `lua5.3 init.lua` diff --git a/cmds/rub.lua b/cmds/rub.lua index cd1c3d8..7bf2508 100644 --- a/cmds/rub.lua +++ b/cmds/rub.lua @@ -8,7 +8,7 @@ } function rub:course(wants, fmt) - local resp, succ = (require'tg.tools').requ(self.url) + local resp, succ = (require'core.tools').requ(self.url) if not succ then return {}, '[ошибка]', {} end @@ -61,4 +61,4 @@ return { args = '[valute]...', desc = 'ruble course', run = rub.msg -} \ No newline at end of file +} diff --git a/tg/api.lua b/core/api.lua similarity index 89% rename from tg/api.lua rename to core/api.lua index cbcfe98..4ff7594 100644 --- a/tg/api.lua +++ b/core/api.lua @@ -1,14 +1,18 @@ --- API Library ---- (c) Er2 ---- Zlib License +--[[ API Library + -- (c) Er2 2021 + -- Zlib License +--]] -local tools = require 'tg.tools' -local json = require 'tg.json' -local api = { - request = function(self, ...) return tools.request(self.token, ...) end, -} +local tools =require 'core.tools' +local json = require 'core.json' +local events=require 'core.events' +local api = { _ev_ = {} } api.__index = api -- Make class +events(api) -- inheritance + +function api:request(...) return tools.request(self.token, ...) end + -- Getters without params function api:getMe() return self:request 'getMe' end @@ -94,4 +98,4 @@ function api:setMyCommands(cmds) return self:request('setMyCommands', { commands = json.encode(cmds) }) end -return api \ No newline at end of file +return api diff --git a/tg/core.lua b/core/core.lua similarity index 58% rename from tg/core.lua rename to core/core.lua index c6c6997..23db15c 100644 --- a/tg/core.lua +++ b/core/core.lua @@ -1,23 +1,19 @@ --- Core file ---- (c) Er2 ---- Zlib License +--[[ Core file + -- (c) Er2 2021 + -- Zlib License +--]] -local tools = require 'tg.tools' -local api = require 'tg.api' -api.__index = api -- Make class +local tools = require 'core.tools' +local api = require 'core.api' +api.__index = api --- EVENT PROTOTYPES -- -function api.onCommand(_) end -function api.onChannelPost(_) end -function api.onChannelPostEdit(_) end -function api.onMessage(_) end -function api.onMessageEdit(_) end -function api.onInlineResult(_) end -function api.onPoll(_) end -function api.onPollAnswer(_) end -function api.onReady(_) end -function api.onQuery(_) end -function api.onUpdate(_) end +function api:_ev(t, i, name, ...) + local v = t[i] + if v.name == name then + v.fn(self, ...) + if v.type == 'once' then table.remove(t, i) end + end +end -- UPDATES -- function api:getUpdates(lim, offs, tout, allowed) @@ -31,7 +27,7 @@ function api:getUpdates(lim, offs, tout, allowed) end local function receiveUpdate(self, update) - if update then self:onUpdate(update) end + if update then self:emit('update', update) end if update.message then local txt = update.message.text @@ -43,26 +39,27 @@ local function receiveUpdate(self, update) update.message.cmd = cmd update.message.args = args - return self:onCommand(update.message, update.message.chat.type) + + return self:emit('command', update.message) elseif cmd then return end - self:onMessage(update.message, update.message.chat.type) + self:emit('message', update.message) elseif update.edited_message then - self:onMessageEdit(update.edited_message, update.edited_message.chat.type) + self:emit('messageEdit', update.edited_message) - elseif update.channel_post then self:onChannelPost(update.channel_post) - elseif update.edited_channel_post then self:onChannelPostEdit(update.edited_channel_post) + elseif update.channel_post then self:emit('channelPost', update.channel_post) + elseif update.edited_channel_post then self:emit('channelPostEdit', update.edited_channel_post) - elseif update.poll then self:onPoll(update.poll) - elseif update.poll_answer then self:onPollAnswer(update.poll_answer) + elseif update.poll then self:emit('poll', update.poll) + elseif update.poll_answer then self:emit('pollAnswer', update.poll_answer) - elseif update.callback_query then self:onQuery('callback', update.callback_query) - elseif update.inline_query then self:onQuery('inline', update.inline_query) - elseif update.shipping_query then self:onQuery('shipping', update.shipping_query) - elseif update.pre_checkout_query then self:onQuery('preCheckout', update.pre_checkout_query) + elseif update.callback_query then self:emit('callbackQuery', update.callback_query) + elseif update.inline_query then self:emit('inlineQuery', update.inline_query) + elseif update.shipping_query then self:emit('shippingQuery', update.shipping_query) + elseif update.pre_checkout_query then self:emit('preCheckoutQuery', update.pre_checkout_query) - elseif update.chosen_inline_result then self:onInlineResult(update.chosen_inline_result) + elseif update.chosen_inline_result then self:emit('inlineResult', update.chosen_inline_result) end end @@ -91,7 +88,7 @@ function api:run(lim, offs, tout, al) tout = tonumber(tout) or 0 self.runs = true - self:onReady() + self:emit('ready') self.co = coroutine.create(api._loop) coroutine.resume(self.co, self, lim, tout, offs, al) @@ -117,6 +114,7 @@ end return function(opts) if not token or type(token) ~= 'string' then token = nil end + local self = setmetatable({}, api) if not opts then return self end @@ -124,4 +122,4 @@ return function(opts) if opts.norun then self.nr = true end return self -end \ No newline at end of file +end diff --git a/core/events.lua b/core/events.lua new file mode 100644 index 0000000..a44e09d --- /dev/null +++ b/core/events.lua @@ -0,0 +1,39 @@ +--[[ Events library + -- (c) Er2 2021 + -- Zlib License +--]] + +local events = {} +events.__index = events + +function events:_add(t, n, f) + table.insert(self._ev_, { + type = t, + name = n, + fn = f, + }) +end + +function events:on(n,f) self:_add('on', n,f) end +function events:once(n,f) self:_add('once', n,f) end + +function events:_ev(t, i, name, ...) + local v = t[i] + if v.name == name then + v.fn(...) + if v.type == 'once' then table.remove(t, i) end + end +end + +function events:emit(name, ...) + local t = self._ev_ + for i = 1, #t do + local v = t[i] or {} + if type(v) == 'table' + and type(v.type) == 'string' + and type(v.fn) == 'function' + then self:_ev(t, i, name, ...) end + end +end + +return function(t) return setmetatable(t, events) end diff --git a/core/init.lua b/core/init.lua new file mode 100644 index 0000000..9855539 --- /dev/null +++ b/core/init.lua @@ -0,0 +1 @@ +return require 'core.core' diff --git a/tg/json.lua b/core/json.lua similarity index 100% rename from tg/json.lua rename to core/json.lua diff --git a/tg/tools.lua b/core/tools.lua similarity index 64% rename from tg/tools.lua rename to core/tools.lua index a34462f..c877d6e 100644 --- a/tg/tools.lua +++ b/core/tools.lua @@ -1,5 +1,10 @@ +--[[ Additional tools + -- (c) Er2 2021 + -- Zlib license +--]] + local tools = { - json = require 'tg.json', + json = require 'core.json', } local json = tools.json @@ -7,11 +12,9 @@ local https = require 'ssl.https' local ltn12 = require 'ltn12' function tools.fetchCmd(text) - local cmd = text:match '/[%w_]+' - local to = text:match '/[%w_]+(@[%w_]+)' - if to then to = to:sub(2) end - if cmd then cmd = cmd:sub(2) end - return cmd, to + return + text:match '/([%w_]+)', -- cmd + text:match '/[%w_]+@([%w_]+)' -- to end -- https://gist.github.com/liukun/f9ce7d6d14fa45fe9b924a3eed5c3d99 @@ -23,7 +26,14 @@ function tools.urlencode(url) return url end -function tools.req(url) +function tools.req(url, par) + if type(par) == 'table' then + url = url .. '?' + for k,v in pairs(par) do + url = url ..'&'.. k ..'='.. tools.urlencode(tostring(v)) + end + end + local resp = {} local succ, res = https.request { url = url, @@ -38,9 +48,9 @@ function tools.req(url) return resp[1], true end -function tools.requ(url, dbg) - local res, succ = tools.req(url) - if dbg then print(succ, res) end +function tools.requ(url, par, dbg) + local res, succ = tools.req(url, par) + if dbg then print(url, succ, res) end res = json.decode(res or '{}') if not succ or not res then return {}, false end return res, true @@ -50,16 +60,11 @@ function tools.request(token, endpoint, param, dbg) assert(token, 'Provide token!') assert(endpoint, 'Provide endpoint!') - local params = '' - for k, v in pairs(param or {}) do - params = params .. '&' .. k .. '=' .. tools.urlencode(tostring(v)) - end - local url = 'https://api.telegram.org/bot' .. token .. '/' .. endpoint - if #params > 1 then url = url .. '?' .. params:sub(2) end - local resp = tools.requ(url, dbg) + -- dbg = true + local resp = tools.requ(url, param, dbg) return resp, resp.ok or false end -return tools \ No newline at end of file +return tools diff --git a/db/.gitignore b/db/.gitignore index becae58..bbd1c80 100644 --- a/db/.gitignore +++ b/db/.gitignore @@ -1,4 +1,4 @@ * !.gitignore -!db.lua -!mp.lua \ No newline at end of file +!init.lua +!mp.lua diff --git a/db/db.lua b/db/init.lua similarity index 100% rename from db/db.lua rename to db/init.lua diff --git a/init.lua b/init.lua index 701bfe9..a4788be 100644 --- a/init.lua +++ b/init.lua @@ -1,8 +1,8 @@ local config = require 'config' local Core = { - db = require 'db.db' ('db'), - tg = require 'tg', + db = require 'db' ('db'), -- db with name db + tg = require 'core', tools = tools, config = config, cmds = {}, @@ -14,33 +14,44 @@ function Core:load(what) local s = #c for i = 1, s do local v = c[i] - print(('Loading %s (%d / %d) %s...'):format(what:sub(0, -2), i, s, v)) - if not pcall(require, what .. '.' .. v) then print 'fail'; goto f end - local a = require(what .. '.' .. v) - if what == 'events' then - self.api['on' .. v:sub(1, 1):upper() .. v:sub(2)] = function(...) - local succ = pcall(a, self, ...) - if not succ then print('event ' .. v .. ' was failed') end + print(('Loading %s (%d / %d) %s...'):format(what:sub(0, -2), i, s, v)) + -- Lint + if pcall(require, what ..'.'.. v) then + local a=require(what ..'.'.. v) + if what == 'events' then self.api:on(v, a) + elseif what == 'cmds' then self.cmds[v] = a end - elseif what == 'cmds' then self.cmds[v] = a - end - ::f:: + else print 'fail' end end print(('Loaded %d %s'):format(s, what)) 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 + if v.type == 'once' then table.remove(t, i) end + end +end + function Core:init() self.api = tg {norun = true} print 'Client initialization...' + self.api._ev = function(s, t, i, name, ...) + -- print(s, t, i, name) + self:ev(t, i, name, s, ...) + end + self:load 'events' self.api:login(config.token, function() print('Logged on as @' .. self.api.info.username) self.config.token = nil - self.api:onReady() + self.api:emit('ready') end) print 'Done!' @@ -61,4 +72,4 @@ function Core:init() self.api:getUpdates(1, offs, 0) end -Core:init() \ No newline at end of file +Core:init() diff --git a/tg/init.lua b/tg/init.lua deleted file mode 100644 index 6cad5d0..0000000 --- a/tg/init.lua +++ /dev/null @@ -1 +0,0 @@ -return require 'tg.core' \ No newline at end of file