add readme, some code refactoring

This commit is contained in:
Er2 2021-07-19 10:55:01 +03:00
parent 7f68524d5a
commit 49fa684f47
13 changed files with 162 additions and 80 deletions

View file

@ -1,4 +1,4 @@
zlib License Zlib License
Copyright (c) 2021 Er2 <er2@dismail.de> Copyright (c) 2021 Er2 <er2@dismail.de>

25
README.md Normal file
View file

@ -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`

View file

@ -8,7 +8,7 @@
} }
function rub:course(wants, fmt) 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 if not succ then
return {}, '[ошибка]', {} return {}, '[ошибка]', {}
end end
@ -61,4 +61,4 @@ return {
args = '[valute]...', args = '[valute]...',
desc = 'ruble course', desc = 'ruble course',
run = rub.msg run = rub.msg
} }

View file

@ -1,14 +1,18 @@
-- API Library --[[ API Library
--- (c) Er2 <er2@dismail.de> -- (c) Er2 2021 <er2@dismail.de>
--- Zlib License -- Zlib License
--]]
local tools = require 'tg.tools' local tools =require 'core.tools'
local json = require 'tg.json' local json = require 'core.json'
local api = { local events=require 'core.events'
request = function(self, ...) return tools.request(self.token, ...) end, local api = { _ev_ = {} }
}
api.__index = api -- Make class api.__index = api -- Make class
events(api) -- inheritance
function api:request(...) return tools.request(self.token, ...) end
-- Getters without params -- Getters without params
function api:getMe() return self:request 'getMe' end function api:getMe() return self:request 'getMe' end
@ -94,4 +98,4 @@ function api:setMyCommands(cmds)
return self:request('setMyCommands', { commands = json.encode(cmds) }) return self:request('setMyCommands', { commands = json.encode(cmds) })
end end
return api return api

View file

@ -1,23 +1,19 @@
-- Core file --[[ Core file
--- (c) Er2 <er2@dismail.de> -- (c) Er2 2021 <er2@dismail.de>
--- Zlib License -- Zlib License
--]]
local tools = require 'tg.tools' local tools = require 'core.tools'
local api = require 'tg.api' local api = require 'core.api'
api.__index = api -- Make class api.__index = api
-- EVENT PROTOTYPES -- function api:_ev(t, i, name, ...)
function api.onCommand(_) end local v = t[i]
function api.onChannelPost(_) end if v.name == name then
function api.onChannelPostEdit(_) end v.fn(self, ...)
function api.onMessage(_) end if v.type == 'once' then table.remove(t, i) end
function api.onMessageEdit(_) end end
function api.onInlineResult(_) end end
function api.onPoll(_) end
function api.onPollAnswer(_) end
function api.onReady(_) end
function api.onQuery(_) end
function api.onUpdate(_) end
-- UPDATES -- -- UPDATES --
function api:getUpdates(lim, offs, tout, allowed) function api:getUpdates(lim, offs, tout, allowed)
@ -31,7 +27,7 @@ function api:getUpdates(lim, offs, tout, allowed)
end end
local function receiveUpdate(self, update) local function receiveUpdate(self, update)
if update then self:onUpdate(update) end if update then self:emit('update', update) end
if update.message then if update.message then
local txt = update.message.text local txt = update.message.text
@ -43,26 +39,27 @@ local function receiveUpdate(self, update)
update.message.cmd = cmd update.message.cmd = cmd
update.message.args = args update.message.args = args
return self:onCommand(update.message, update.message.chat.type)
return self:emit('command', update.message)
elseif cmd then return end elseif cmd then return end
self:onMessage(update.message, update.message.chat.type) self:emit('message', update.message)
elseif update.edited_message then 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.channel_post then self:emit('channelPost', update.channel_post)
elseif update.edited_channel_post then self:onChannelPostEdit(update.edited_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 then self:emit('poll', update.poll)
elseif update.poll_answer then self:onPollAnswer(update.poll_answer) elseif update.poll_answer then self:emit('pollAnswer', update.poll_answer)
elseif update.callback_query then self:onQuery('callback', update.callback_query) elseif update.callback_query then self:emit('callbackQuery', update.callback_query)
elseif update.inline_query then self:onQuery('inline', update.inline_query) elseif update.inline_query then self:emit('inlineQuery', update.inline_query)
elseif update.shipping_query then self:onQuery('shipping', update.shipping_query) elseif update.shipping_query then self:emit('shippingQuery', update.shipping_query)
elseif update.pre_checkout_query then self:onQuery('preCheckout', update.pre_checkout_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
end end
@ -91,7 +88,7 @@ function api:run(lim, offs, tout, al)
tout = tonumber(tout) or 0 tout = tonumber(tout) or 0
self.runs = true self.runs = true
self:onReady() self:emit('ready')
self.co = coroutine.create(api._loop) self.co = coroutine.create(api._loop)
coroutine.resume(self.co, self, lim, tout, offs, al) coroutine.resume(self.co, self, lim, tout, offs, al)
@ -117,6 +114,7 @@ end
return function(opts) return function(opts)
if not token or type(token) ~= 'string' then token = nil end if not token or type(token) ~= 'string' then token = nil end
local self = setmetatable({}, api) local self = setmetatable({}, api)
if not opts then return self end if not opts then return self end
@ -124,4 +122,4 @@ return function(opts)
if opts.norun then self.nr = true end if opts.norun then self.nr = true end
return self return self
end end

39
core/events.lua Normal file
View file

@ -0,0 +1,39 @@
--[[ Events library
-- (c) Er2 2021 <er2@dismail.de>
-- 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

1
core/init.lua Normal file
View file

@ -0,0 +1 @@
return require 'core.core'

View file

@ -1,5 +1,10 @@
--[[ Additional tools
-- (c) Er2 2021 <er2@dismail.de>
-- Zlib license
--]]
local tools = { local tools = {
json = require 'tg.json', json = require 'core.json',
} }
local json = tools.json local json = tools.json
@ -7,11 +12,9 @@ local https = require 'ssl.https'
local ltn12 = require 'ltn12' local ltn12 = require 'ltn12'
function tools.fetchCmd(text) function tools.fetchCmd(text)
local cmd = text:match '/[%w_]+' return
local to = text:match '/[%w_]+(@[%w_]+)' text:match '/([%w_]+)', -- cmd
if to then to = to:sub(2) end text:match '/[%w_]+@([%w_]+)' -- to
if cmd then cmd = cmd:sub(2) end
return cmd, to
end end
-- https://gist.github.com/liukun/f9ce7d6d14fa45fe9b924a3eed5c3d99 -- https://gist.github.com/liukun/f9ce7d6d14fa45fe9b924a3eed5c3d99
@ -23,7 +26,14 @@ function tools.urlencode(url)
return url return url
end 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 resp = {}
local succ, res = https.request { local succ, res = https.request {
url = url, url = url,
@ -38,9 +48,9 @@ function tools.req(url)
return resp[1], true return resp[1], true
end end
function tools.requ(url, dbg) function tools.requ(url, par, dbg)
local res, succ = tools.req(url) local res, succ = tools.req(url, par)
if dbg then print(succ, res) end if dbg then print(url, succ, res) end
res = json.decode(res or '{}') res = json.decode(res or '{}')
if not succ or not res then return {}, false end if not succ or not res then return {}, false end
return res, true return res, true
@ -50,16 +60,11 @@ function tools.request(token, endpoint, param, dbg)
assert(token, 'Provide token!') assert(token, 'Provide token!')
assert(endpoint, 'Provide endpoint!') 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 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 return resp, resp.ok or false
end end
return tools return tools

4
db/.gitignore vendored
View file

@ -1,4 +1,4 @@
* *
!.gitignore !.gitignore
!db.lua !init.lua
!mp.lua !mp.lua

View file

@ -1,8 +1,8 @@
local config = require 'config' local config = require 'config'
local Core = { local Core = {
db = require 'db.db' ('db'), db = require 'db' ('db'), -- db with name db
tg = require 'tg', tg = require 'core',
tools = tools, tools = tools,
config = config, config = config,
cmds = {}, cmds = {},
@ -14,33 +14,44 @@ function Core:load(what)
local s = #c local s = #c
for i = 1, s do for i = 1, s do
local v = c[i] 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) print(('Loading %s (%d / %d) %s...'):format(what:sub(0, -2), i, s, v))
if what == 'events' then -- Lint
self.api['on' .. v:sub(1, 1):upper() .. v:sub(2)] = function(...) if pcall(require, what ..'.'.. v) then
local succ = pcall(a, self, ...) local a=require(what ..'.'.. v)
if not succ then print('event ' .. v .. ' was failed') end if what == 'events' then self.api:on(v, a)
elseif what == 'cmds' then self.cmds[v] = a
end end
elseif what == 'cmds' then self.cmds[v] = a else print 'fail' end
end
::f::
end end
print(('Loaded %d %s'):format(s, what)) print(('Loaded %d %s'):format(s, what))
end 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() function Core:init()
self.api = tg {norun = true} self.api = tg {norun = true}
print 'Client initialization...' 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:load 'events'
self.api:login(config.token, function() self.api:login(config.token, function()
print('Logged on as @' .. self.api.info.username) print('Logged on as @' .. self.api.info.username)
self.config.token = nil self.config.token = nil
self.api:onReady() self.api:emit('ready')
end) end)
print 'Done!' print 'Done!'
@ -61,4 +72,4 @@ function Core:init()
self.api:getUpdates(1, offs, 0) self.api:getUpdates(1, offs, 0)
end end
Core:init() Core:init()

View file

@ -1 +0,0 @@
return require 'tg.core'