Compare commits

...

27 commits
v05 ... main

Author SHA1 Message Date
Er2
ed6598b145 Update
Rewritten back to Lua.

Using more beautiful classes.

Add Arabic (lol idk why).
2022-08-10 12:51:49 +03:00
Er2
5a54bc1032 add -off in reload 2022-03-01 11:15:33 +03:00
Er2
0b2a7ac8e5 update lib, add some random 2022-02-28 10:17:49 +03:00
Er2
daa848cecd bugfixes, redebalo 2022-02-27 23:29:37 +03:00
Er2
6c0f8b437d more reply reactions and match whole words, replace Unicode library 2022-02-27 13:22:07 +03:00
Er2
14ba3d7a00 update readme, fix .gitignore 2022-02-26 10:08:26 +03:00
Er2
455869fa0c moonscript rewrite 2022-02-25 23:25:21 +03:00
Er2
7e8b53a207 remade message event, add utf8 2022-02-25 19:58:53 +03:00
Er2
17f1727be1 ??? 2022-02-25 12:54:26 +03:00
Er2
b0f616fd32 add хуех on эх, start command 2022-02-25 10:31:32 +03:00
Er2
7e26dfa38d add all valutes in rub 2022-02-16 10:49:40 +03:00
Er2
6add3f5e6b update eval, pattern escape function 2022-02-16 10:35:11 +03:00
Er2
8f538ba65a remove db and strange unicode at start 2022-02-13 18:58:17 +03:00
Er2
3bbccb94ab optional quoted args, update lib 2022-02-13 15:23:16 +03:00
Er2
65b2f6eebc fix reload, new valute 2022-02-13 15:19:47 +03:00
Er2
70da2d717e upgrade lib 2022-02-12 21:48:19 +03:00
Er2
2b72d1c98d update lib, remove depend() in rc file 2022-02-02 18:52:28 +03:00
Er2
dffe9cc9b2 restored localized commands 2022-01-23 21:44:39 +03:00
Er2
f50fe9e35b update tg lib 2022-01-23 18:23:54 +03:00
Er2
c7c2ccc664 luajit support 2022-01-23 18:18:53 +03:00
Er2
18a36e169a update readme 2022-01-18 20:21:34 +00:00
Er2
1b498be1d6 now must reload 2022-01-18 19:48:06 +00:00
Er2
8421b61a82 aww 2022-01-18 19:37:23 +00:00
Er2
5dee01b07b reload 2022-01-18 19:34:23 +00:00
Er2
9a9bd56b7d use floats 2022-01-18 19:15:03 +03:00
Er2
00b15b847b add uptime 2022-01-18 18:50:00 +00:00
Er2
214b621cdf openrc script, some changes 2022-01-18 18:25:18 +03:00
32 changed files with 2544 additions and 285 deletions

4
.dockerignore Normal file
View file

@ -0,0 +1,4 @@
.env
.gitignore
.git

2
.gitignore vendored
View file

@ -2,3 +2,5 @@
*~
\#*#
.env

3
.gitmodules vendored
View file

@ -2,3 +2,6 @@
path = etc/api
url = https://gitdab.com/er2/tg-api-lua
[submodule "etc/utf8"]
path = etc/utf8
url = https://github.com/Stepets/utf8.lua

21
Dockerfile Normal file
View file

@ -0,0 +1,21 @@
FROM alpine AS build
RUN apk add luarocks5.1 libressl-dev gcc musl-dev lua5.1-dev \
&& luarocks-5.1 install luasec \
&& mkdir -p /dist/usr/local \
&& mkdir -p /dist/usr/local/lib/lua && cp -rpv /usr/local/lib/lua/* /dist/usr/local/lib/lua \
&& mkdir -p /dist/usr/local/share/lua && cp -rpv /usr/local/share/lua/* /dist/usr/local/share/lua
COPY run.sh /dist
COPY . /dist/app
FROM alpine
VOLUME ["/app"]
RUN apk add libressl luajit \
&& ln -sf $(which luajit) /usr/bin/lua \
&& adduser -D -h /app user
COPY --from=build /dist /
ENTRYPOINT ["/run.sh"]

View file

@ -1,15 +1,19 @@
return {
token = 'atokenyanedam',
owner = 935626920 , -- hehe
token = os.getenv 'TOKEN',
owner = tonumber(os.getenv 'OWNER'),
cmds = {
'eval',
'reload',
'ping',
'rub',
'start',
'shell',
},
events = {
'command',
'ready',
'inlineQuery',
'message',
},
parts = {
'locale',

@ -1 +1 @@
Subproject commit 30f7203c5fe69e875dbd29a4da9fe0c5ab1ddc9b
Subproject commit faab5a7e6d0dc684d04cf5121b9735b786716aca

116
etc/class.lua Normal file
View file

@ -0,0 +1,116 @@
-- Class library
-- (c) Er2 2022 <er2@dismail.de>
-- Zlib License
-- (Not) Virtual classes table
local vtable = {}
-- Metamethods
local cls = {}
cls.__index = cls
-- :__tostring() - Calls when using Lua tostring() or error occurs.
function cls:__tostring()
local str = 'Class <'.. self.__name ..'>'
while self.__super do
str = str.. ' inherits '.. self.__super.__name
self = self.__super
end
return str
end
-- :__call(table) - Calls when defining class body
function cls:__call(t)
for k, v in pairs(t) do
self[k] = v
end
if self.__init
then self:__init()
end
end
-- :new(...) - Creates new instance of class. Please use new(...) function instead.
-- Can throw error if no constructor defined in class or subclasses.
function cls:new(...)
local cl = self
local cons
repeat cons = rawget(cl, 1) or rawget(cl, 'new')
cl = cl.__super
until cons or not cl
assert(cons, 'No constructor found in class')
--self.__index = self.__index or cls.__index
self = setmetatable({}, self)
cons(self, ...)
return self
end
-- :super(...) - Call parent constructor.
function cls:super(...)
return cls.superM(self, 1, ...)
or cls.superM(self, 'new', ...)
end
-- :superM(method, ...) - Call method from parent class.
-- Can throw error if class is not inherited.
function cls:superM(meth, ...)
local cl = self.__super
local fn
while not fn and cl do
fn = cl[meth]
if cl.__sup then fn = nil end
cl = cl.__super
end
if not fn then return nil end
if cl then cl.__sup = true end
local v = {fn(self, ...)}
if cl then cl.__sup = nil end
return table.unpack(v)
end
-- :clone() - Make clone of class.
function cls:clone()
local cl = {}
for k, v in pairs(cls)
do cl[k] = v end
for k, v in pairs(self)
do cl[k] = v end
cl.__index = cl
cl.__super = self
return setmetatable(cl, self)
end
-- :inherits(className) - Make this class inherits from another.
-- Can throw error if class is already inherited.
function cls:inherits(name)
-- assert(self.__super, 'no')
local cl = vtable[name]
assert(cl, 'Class is not exists')
return setmetatable(self, cl:clone())
end
-- :extends(className) - Same as :inherits(className).
cls.extends = cls.inherits
-- class(className) - Make new class.
-- Can throw error if class already exists with this name.
function class(name)
assert(not vtable[name], 'Cannot override defined class')
local cl = setmetatable({__name = name, __tostring = cls.__tostring}, cls)
cl.__index = cl
vtable[name] = cl
return cl
end
-- new(className) - Make new instance of class. Use this instead of :new(class).
-- Can throw error if class by name was not found.
function new(name)
local cl = vtable[name]
assert(cl, 'No class found')
return function(...)
return cls.new(cl, ...)
end
end

View file

@ -1,43 +1,74 @@
--[[ Events library
-- (c) Er2 2021 <er2@dismail.de>
-- Zlib License
--]]
-- Events library
-- (c) Er2 2022 <er2@dismail.de>
-- Zlib License
local events = {}
events.__index = events
require 'class'
function events:_add(t, n, f)
table.insert(self._ev_, {
type = t,
name = n,
fn = f,
})
end
class 'Events' {
function(this)
this._ev_ = {}
end,
function events:on(n,f) self:_add('on', n,f) end
function events:once(n,f) self:_add('once', n,f) end
_add = function(this, type, name, func)
table.insert(this._ev_, {
type = tostring(type),
name = tostring(name),
func = func,
})
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
on = function(this, name, func)
this:_add('on', name, func)
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.name) == 'string'
and type(v.type) == 'string'
and type(v.fn) == 'function'
then self:_ev(t, i, name, ...) end
end
end
once = function(this, name, func)
this:_add('once', name, func)
end,
off = function(this, func)
for k, v in pairs(this._ev_) do
if v.func == func
then table.remove(this._ev_, k)
end
end
end,
_ev = function(this, t, i, name, ...)
local v = t[i]
if v.name == name then
v.func(...)
if v.type == 'once'
then table.remove(t, i)
end
end
end,
emit = function(this, name, ...)
local t = this._ev_
for i = 1, #t do
local v = t[i]
if type(v) == 'table'
and type(v.name) == 'string'
and type(v.type) == 'string'
and type(v.func) == 'function'
then this:_ev(t, i, name, ...)
else print 'Invalid event'
if v then print(v, v.name, v.type, v.func)
else print 'nil' end
end
end
end,
}
class 'EventsThis' : inherits 'Events' {
_ev = function(this, t, i, name, ...)
local v = t[i]
if v.name == name then
v.func(this, ...)
if v.type == 'once'
then table.remove(t, i)
end
end
end,
}
return function(t)
t._ev_ = {}
return setmetatable(t, events)
end

1
etc/utf8 Submodule

@ -0,0 +1 @@
Subproject commit 17f4e009a22fb2f2e6ad316a05b2cca8e071fc3b

1860
etc/utf8data.lua Normal file

File diff suppressed because it is too large Load diff

3
example.env Normal file
View file

@ -0,0 +1,3 @@
TOKEN= # insert bot token here
OWNER= # insert owner ID here

View file

@ -1,3 +1,3 @@
package.path = 'etc/?.lua;etc/?/init.lua;' .. package.path
package.path = 'etc/?.lua;etc/?/init.lua;?.lua;?/init.lua;' .. package.path
require 'src.parts.core'

View file

@ -1,6 +1,6 @@
Zlib License
Copyright (c) 2021 Er2 <er2@dismail.de>
Copyright (c) 2022 Er2 <er2@dismail.de>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages

View file

@ -1,38 +1,33 @@
* Computer bot
Now, GitHub contains *mirror*, original is *[[https://gitdab.com/er2/comp-tg][here]]*.
Original is *[[https://gitdab.com/er2/comp-tg][here]]* and [[https://git.er2.tech/er2/comp-tg][here]].
Mirrors can update irregularly.
-----
This bot is a continue of [[https://github.com/Er2pkg/computer][my Discord bot]],
but on Telegram.
This is a bot for Telegram.
It haven't got much possibilities,
but this code can be used in other bots.
Bot can use locale of Telegram client (unlike much bots)
and use it or fallback to English.
Bot uses an OOP-style of Lua
as [[https://is.gd/f0Vadk][described on Wikipedia]].
Also bot reads client language and automatically loads needing locale.
Maybe I rewrite bot's core to C but there already so many Lua code.
Maybe I will rewrite bot's core to C but here already so many Lua code.
* Installation
[[https://alpinelinux.org][Alpine linux]], root:
Officially supported only [[https://docker.com][Docker]], but you can use it without Docker.
+ Enable community repo (described in wiki)
Docker installation:
+ Install lua and tools: ~apk add sudo git lua5.3 luarocks openssl-dev~
+ Clone repository with submodules: ~git clone --recursive https://gitdab.com/er2/comp-tg~
+ Install LuaSec for https requests: ~luarocks-5.3 install luasec~
and enter ~cd comp-tg~
+ Create user: ~adduser user~
+ Build image: ~docker build -t comp-tg .~
and login to this user
+ Clone repo: ~git clone --recursive https://gitdab.com/er2/comp-tg~
and enter ~cd comp-tg~
+ Change token and owner in *config.lua*
+ Run: ~lua5.3 init.lua~
+ Start it: ~docker run -d -e .env --name comp-tg --restart always comp-tg~

7
run.sh Executable file
View file

@ -0,0 +1,7 @@
#!/bin/sh
chown user:user -R /app 2> /dev/null
cd /app
su user -c "lua init.lua"

View file

@ -1,11 +1,12 @@
local function prind(...)
local t = {...}
local s = ''
for i = 1, #t do
if i > 1 then s = s..'\t' end
s = s .. tostring(t[i] or 'nil')
for _, v in pairs {...} do
if #s > 0
then s = s .. '\t'
end
s = s .. tostring(v) or 'nil'
end
return s .. '\n'
return s ..'\n'
end
local env = {
@ -19,11 +20,9 @@ local env = {
type = type,
pcall = pcall,
xpcall = xpcall,
math = math,
string = string,
table = table,
dump = dump,
}
@ -33,17 +32,29 @@ return {
local s = ''
local t = {
msg = msg,
print = function(...) s = s .. prind(...) end,
print = function(...)
s = s .. prind(...)
end,
C = owner and C or nil,
api = owner and C.api or nil,
api = owner and C.api or nil
}
for k,v in pairs(env) do t[k] = v end
local e, err = load(table.concat(msg.args, ' '), 'eval', 't', t)
xpcall(function()
if err then error(err) end
for k, v in pairs(env)
do t[k] = v
end
local e, err = load(C.api.unparseArgs(msg.args), 'eval', 'bt', t)
xpcall((function()
if err
then error(err)
end
e = tostring(e() or '...')
end, function(err) e = err end)
C.api:send(msg, s .. '\n' .. e)
end), function(err)
e = err
end)
s = s ..'\n'.. e
s = s:gsub(C.api.token:escp(), '<TOKEN>')
C.api:reply(msg, s)
end
}
}

View file

@ -1,5 +1,11 @@
return {
run = function(C, msg)
C.api:send(msg, msg.loc.pat:format(os.time() - msg.date))
local t = os.time()
local ps, ls = t - msg.date, t - C.loaded
local lm = ls / 60
local lh = lm / 60
local ld = lh / 24
C.api:send(msg, msg.loc.pat:format(ps, ld, lh, lm, ls))
end
}
}

32
src/cmds/reload.lua Normal file
View file

@ -0,0 +1,32 @@
return {
private = true,
run = function(C, msg)
local cat, sub, arg = table.unpack(msg.args)
if not (cat and sub)
then return C.api:reply(msg, '/reload cmds ping')
end
local path = 'src.'.. cat ..'.'.. sub
C.api:off(package.loaded[path])
package.loaded[path] = nil
if arg == '-off'
then C.api:reply(msg, 'Turned off')
else
local suc, m = pcall(require, path)
if not suc
then return C.api:reply(msg, 'Reload failed. '.. m)
end
if cat == 'events'
then C.api:on(sub, m)
elseif cat == 'cmds'
then C.cmds[sub] = m
else m(C)
end
C.api:reply(msg, 'Reloaded. '.. tostring(m))
end
end
}

View file

@ -1,61 +1,91 @@
-- It uses data from central bank of Russia
--- and external service to get json from xml
-- Privacy and security is unknown
class 'Rub' {
url = 'https://api.factmaven.com/xml-to-json/?xml=https://www.cbr.ru/scripts/XML_daily.asp',
pat = '%d %s (%s) - %f ₽',
local rub = {
url = 'https://api.factmaven.com/xml-to-json/?xml='
.. 'https://www.cbr.ru/scripts/XML_daily.asp',
function(this)
this.tools = require 'api.tools'
end,
tools = require 'etc.api.tools',
getPat = function(this, val)
return this.pat:format(val.Nominal, val.Name, val.CharCode, val.Value:gsub(',', '.'))
end,
course = function(this, wants)
local res, ok = this.tools._req(this.url, 'GET')
if not ok
then return 'err'
end
res = this.tools.json.decode(res or '{}')
res = res.ValCurs
if not res
then return 'err'
end
-- Pseudo-valutes
table.insert(res.Valute, {
ID = 'R01000',
NumCode = '001',
CharCode = 'RUB',
Nominal = 1,
Name = 'Российский рубль',
Value = '1',
})
local uah = table.findV(res.Valute, {CharCode = 'UAH'})
assert(uah, 'No UAH found')
table.insert(res.Valute, {
ID = 'R02000',
NumCode = '200',
CharCode = 'SHT',
Nominal = 1,
Name = 'Штаны',
Value = ('%f'):format(tonumber(uah.Value:gsub(',', '.'), nil) / uah.Nominal * 40), -- 40 UAH
})
local r, founds = {}, {}
if table.find(wants, 'ALL') then
for _, v in pairs(res.Valute)
do table.insert(r, this:getPat(v))
end
return r, res.Date, wants -- string, date, found
end
for _, v in pairs(res.Valute) do
if table.find(wants, v.CharCode) then
table.insert(founds, v.CharCode)
table.insert(r, this:getPat(v))
end
end
return r, res.Date, founds --
end,
}
function rub:course(wants)
local resp, succ = self.tools._req(self.url, 'GET')
if not succ then return 'err' end
resp = self.tools.json.decode(resp or '{}')
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, ('%d %s (%s) - %f ₽'):format(v.Nominal, v.Name, v.CharCode, v.Value:gsub(',', '.')))
end
end
return r, resp.Date, founds
end
local rub = new 'Rub' ()
return {
run = function(C, msg)
local wants = {'USD', 'EUR', table.unpack(msg.args)}
for i = 1, #wants do wants[i] = wants[i]:upper() end -- uppercase
run = function(self, 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)
if v == 'error' then
return C.api:reply(msg, C.locale:get('error', 'req_err', msg.l))
if v == 'err' then
return self.api:reply(msg, self.locale:get('error', 'req_err', msg.l))
end
local nf = {}
local nf = { }
for _, i in pairs(wants) do
if not table.find(f, i) then table.insert(nf, i) end
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)
if #nf > 0 then
s = s .. msg.loc.notf .. table.concat(nf, ',')
end
self.api:reply(msg, s .. msg.loc.prov)
end
}

16
src/cmds/shell.lua Normal file
View file

@ -0,0 +1,16 @@
return {
private = true,
run = function(C, msg)
local file = io.popen(C.api.unparseArgs(msg.args))
if file then
local r = file:read '*a'
file:close()
if #r == 0
then r = '...'
end
C.api:reply(msg, r)
else return C.api:reply(msg, 'error')
end
end
}

View file

@ -1,6 +1,7 @@
return {
hide = true,
run = function(C, msg)
C.api:reply(msg, 'TODO!')
end
}
C.api:reply(msg, msg.loc.msg)
end,
}

View file

@ -1,28 +1,29 @@
return function(C, api, msg)
local cmd = C.cmds[msg.cmd]
local owner = msg.from.id == C.config.owner
return function(C, api, msg)
local l = msg.from.language_code
local owner = msg.from.id == C.config.owner
local cmd = C.cmds[msg.cmd]
msg.l = l
if cmd == nil then
api:send(msg, C.locale:get('error', 'inv_cmd', l))
if not cmd
then api:send(msg, C.locale:get('error', 'inv_cmd', l))
elseif type(cmd.run) ~= 'function' then
api:send(msg, C.locale:get('error', 'cmd_run', l))
elseif cmd.private and not owner then
api:send(msg, C.locale:get('error', 'adm_cmd', l))
elseif type(cmd.run) ~= 'function'
then api:send(msg, C.locale:get('error', 'cmd_run', l))
elseif cmd.private and not owner
then api:send(msg, C.locale:get('error', 'adm_cmd', l))
else
if cmd.useQArgs
then msg.args = api.parseArgs(api.unparseArgs(msg.args))
end
msg.loc = C.locale:get('cmds', msg.cmd, l)
local succ, err = pcall(cmd.run, C, msg, owner)
if not succ then
local suc, err = pcall(cmd.run, C, msg, owner)
if not suc then
print(err)
local cid = C.config.owner
api:forward(cid, msg.chat.id, msg.message_id, false)
api:send(cid, err)
api:forward(C.config.owner, msg, msg.message_id, false)
api:send(C.config.owner, err)
api:reply(msg, C.locale:get('error', 'not_suc', l))
end
end
end

View file

@ -1,3 +1,10 @@
return function(C, api, q)
api.inline:answer(q.id, api.inline.result('photo', '1', 'https://cdn.discordapp.com/attachments/871474214270554124/876506659311202395/unknown.png'):thumb('https://cdn.discordapp.com/attachments/871474214270554124/876506659311202395/unknown.png'))
api.inline:answer(q.id, {
api.inline.result('photo', '1',
'https://cdn.discordapp.com/attachments/871474214270554124/876506659311202395/unknown.png'
):thumb 'https://cdn.discordapp.com/attachments/871474214270554124/876506659311202395/unknown.png',
api.inline.result('photo', '2',
'https://cdn.discordapp.com/attachments/710425649449402408/1002915343116795914/unknown.png'
):thumb 'https://cdn.discordapp.com/attachments/710425649449402408/1002915343116795914/unknown.png',
})
end

22
src/events/message.lua Normal file
View file

@ -0,0 +1,22 @@
local stick = {
{
'AgADwAADcpO1DQ',
'редебало',
'CAACAgIAAx0CUY2umQACFItiHHUg6w_MPu6Vs8k76cwn4OIHNQACwAADcpO1DVbNTDlmHOWMIwQ'
}
}
return function(C, api, msg)
if msg.from.is_premium then
api:reply(msg, 'Премак юзер, надо бы забанить. TODO БЛЯТЬ')
end
if msg.sticker then
for k, v in pairs(stick) do
if msg.sticker.file_unique_id == v[1] then
if math.random() <= 0.5
then api:reply(msg, v[2])
else api:sendSticker(msg, v[3])
end
end
end
end
end

View file

@ -1,46 +1,78 @@
function table.indexOf(t, w)
-- Standard Library in bot
-- table.indexOf(table, value) - Get value of inverter value-key table.
function table.indexOf(t, val)
local i = {}
for k,v in pairs(t) do i[v] = k end
return i[w]
for k, v in pairs(t) do
i[v] = k
end
return i[val]
end
function table.find(t, w)
local i
for k,v in pairs(t) do
if v == w then
i = k
break
-- table.find(table, value) - Return index if value matches.
function table.find(t, val)
for k, v in pairs(t) do
if v == val then
return k
end
end
return i
end
-- table.findV(table, value) - return value in table matching value table
function table.findV(t, val)
local b = nil
for _, v in pairs(t) do
for k, x in pairs(val) do
if x ~= v[k] then
b = 1
break
end
end
if b then
b = nil
else
return v
end
end
end
-- string.escp(string) - Escape reserved regex values
function string.escp(s)
return s:gsub('[%^%$%%%(%)%.%[%]%*%+%-%?]', '%%%0')
end
-- dump(table, depth?) - Return string that shows table contents.
function dump(t, d)
if not tonumber(d) or d < 0 then d = 0 end
if not tonumber(d) or d < 0 then
d = 0
end
local c = ''
for k,v in pairs(t) do
if type(v) == 'table' then v = '\n' .. dump(v, d + 1) end
c = c .. string.format('%s%s = %s\n', (' '):rep(d), k, v)
for k, v in pairs(t) do
if type(v) == 'table' then
v = '\n' .. dump(v, d + 1)
elseif type(v) == 'userdata' then
v = '<USERDATA>'
end
c = c .. ('%s%s = %s\n'):format((' '):rep(d), k, v)
end
return c
end
return function(C, api)
C:load 'cmds'
local a = {}
for k, v in pairs(C.cmds) do
if not (v.private or v.hide) then
local cmd = C.locale:get('cmds', k) or {}
table.insert(a, {
command = k,
description = (cmd.args and cmd.args .. ' - ' or '') .. (cmd.desc or C.locale:get('cmds', 'not_des'))
})
end
end
api:setMyCommands(a)
a = {'levels', }
for i = 1, #a do
if not C.db[a[i]] then C.db[a[i]] = {} end
for _, lang in pairs(C.locale.list) do
local a = {}
for k, v in pairs(C.cmds) do
if not (v.private or v.hide) then
local cmd = C.locale:get('cmds', k, lang or {})
table.insert(a, {
command = k,
description = (cmd.args and cmd.args .. ' - ' or '') .. (cmd.desc or C.locale:get('cmds', 'not_des'))
})
end
end
api:setMyCommands(a, lang)
end
end
end

40
src/locales/ar.json Normal file
View file

@ -0,0 +1,40 @@
{
"error": {
"inv_cmd": "تم تحديد أمر غير معروف.",
"adm_cmd": "لا يمكنك تنفيذ أوامر المسؤول!",
"cmd_run": "لا يمكن تنفيذ هذا الأمر الآن.",
"not_suc": "حدث خطأ وتم إرساله إلى المنشئ.",
"unk_err": "خطأ غير معروف.",
"req_err": "فشل الطلب."
},
"cmds": {
"not_des": "بدون وصف",
"eval": {
"args": "<الرمز>",
"desc": "ينفذ الكود"
},
"shell": {
"args": "<الرمز>",
"desc": "ينفذ التعليمات البرمجية في غلاف يونكس"
},
"ping": {
"desc": "بينج بونج",
"pat": "بونغ! %d ثانية. الجهوزية: %.1f يوم (%.1f ساعة ، %.1f دقيقة)"
},
"rub": {
"args": "[عملة]...",
"desc": "سعر صرف الروبل",
"cur": "متوجها %s:\n%s",
"notf": "\n لا يوجد لا تجد: ",
"prov": "\n البيانات مقدمة من البنك المركزي لروسيا."
},
"start": {
"desc": "بداية العمل",
"msg": "يا! أنا Computer - بوت مفتوح المصدر.\n إذا كنت تبحث عن مصادر: https://gitdab.com/er2/comp-tg"
}
}
}

View file

@ -14,10 +14,14 @@
"args": "<code>",
"desc": "executes code"
},
"shell": {
"args": "<code>",
"desc": "executes code in Unix shell"
},
"ping": {
"desc": "ping pong",
"pat": "Pong! %ds"
"pat": "Pong! %ds. Uptime: %.1f days (%.1fh, %.1fm.)"
},
"rub": {
"args": "[valute]...",
@ -26,6 +30,11 @@
"cur": "Currency at %s:\n%s",
"notf": "\nNot found: ",
"prov": "\nData provided from central bank of Russia."
},
"start": {
"desc": "getting started",
"msg": "Hi! I'm Computer - an open-source bot.\nIf you looking for source code: https://gitdab.com/er2/comp-tg"
}
}
}

View file

@ -14,10 +14,14 @@
"args": "<код>",
"desc": "исполняет код"
},
"shell": {
"args": "<код>",
"desc": "исполняет код в Unix shell"
},
"ping": {
"desc": "пинг-понг",
"pat": "Понг! %d секунд"
"pat": "Понг! %dс. В сети: %.1f дней (%.1fч, %.1fм.)"
},
"rub": {
"args": "[валюта]...",
@ -26,6 +30,11 @@
"cur": "Курс на %s:\n%s",
"notf": "\nНе нашлось: ",
"prov": "\nДанные предоставлены центральным банком России."
},
"start": {
"desc": "начало работы",
"msg": "Привет! Я Computer - бот с открытым исходным кодом.\nЕсли ты ищешь исходники: https://gitdab.com/er2/comp-tg"
}
}
}

View file

@ -1,35 +1,29 @@
local tg = require 'etc.api'
return function(Core)
local self = Core
self.api = tg { norun = true }
self.cmds = {}
require 'etc.api'
return function(C)
C.api = new 'API' {
norun = true,
}
print 'Client initialization...'
function Core._ev(ev, ...) self:ev(...) end
function self.api._ev(_, t, i, n, ...)
self._ev(_, t, i, n, self.api, ...)
C.api._ev = function(_, t, i, name, ...)
return C:_ev(t, i, name, C.api, ...)
end
self:load 'events'
C:load 'events'
self.api:login(self.config.token, function()
print('Logged on as @' .. self.api.info.username)
self.config.token = nil
self.api:emit 'ready'
C.api:login(C.config.token, function()
print('Logged on as @'.. C.api.info.username)
C.config.token = nil
C.api:emit 'ready'
end)
local offs, o = 0
self.api.runs = true
self:on('ready', function()
while self.api.runs do
self:emit 'tick'
o = self.api:_getUpd(1, offs, 0)
offs = o and o or offs
local offset = 0
C.api.runs = true
C:on('ready', function()
while C.api.runs do
C:emit 'tick'
offset = C.api:recvUpdate(1, offset, 0)
end
self.api:getUpdates(1, offs, 0)
end)
end

View file

@ -1,47 +1,62 @@
local config = require 'config'
local config = require 'config'
require 'etc.events'
require 'class'
local Core = {
class 'Core' : inherits 'EventsThis' {
config = config,
}
(require 'etc.events')(Core) -- add events
loaded = 0,
cmds = {},
function Core:load(what)
local c = config[what]
local s = #c
for i = 1, s do
local v = c[i]
function(this)
this:super()
this:load 'parts'
utf8 = require 'etc.utf8'
require 'etc.utf8data'
utf8.config = {
conversion = {
uc_lc = utf8_uc_lc,
lc_uc = utf8_lc_uc
}
}
utf8:init()
print 'Done!'
this:emit 'ready'
end,
print(('Loading %s (%d / %d) %s...'):format(what:sub(0, -2), i, s, v))
-- Lint
if true then
--pcall(require, 'src.'.. what ..'.'.. v) then
local a=require('src.'.. what ..'.'.. v)
if what == 'events' then self.api:on(v, a)
elseif what == 'cmds' then self.cmds[v] = a
elseif what == 'parts' then a(self)
stop = function(this)
this.api:destroy()
print 'Stopped'
print('Uptime: ' .. (os.time() - self.loaded) .. ' seconds')
end,
load = function(this, what)
local c = config[what]
for i = 1, #c do
local v = c[i]
print('Loading ' .. what:sub(0, -2) ..' (' .. i ..' / '.. #c ..') ' .. v ..'...')
-- Lint?
local e, a = pcall(require, 'src.' .. what ..'.'.. v)
print(e, a)
if e then
if what == 'events'
then this.api:on(v, a)
elseif what == 'cmds'
then this.cmds[v] = a
elseif what == 'parts'
then a(this)
end
else print 'fail'
end
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, 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
print('Loaded '.. #c ..' '.. what)
this.loaded = os.time()
end,
}
function Core:init()
self:load 'parts'
local core = new 'Core' ()
print 'Done!'
self:emit 'ready'
end
Core:init()

View file

@ -1,16 +0,0 @@
return function(Core)
local path = 'etc/db' -- from root
local time = 60 * 5 -- 5min
Core.db = require 'etc.db' (path)
local t = os.time()
Core:on('tick', function()
if os.time() - t >= time then
t = os.time()
print 'saving...'
Core.db:save()
end
end)
end

View file

@ -1,33 +1,36 @@
local Locale = {
class 'Locale' {
__newindex = function() end,
list = {
'en',
'ru'
'ru',
'ar',
},
main = 'en',
__newindex = function()end -- ro
__init = function(this)
local json = require 'etc.json'
for i = 1, #this.list do
local n = this.list[i]
local f = io.open('src/locales/'.. n ..'.json')
this[n] = json.decode(f:read 'a')
end
end,
function(this, C)
C.locale = this
end,
get = function(this, category, key, lang)
assert(category, 'Category not provided')
assert(key, 'Key not provided')
lang = lang or this.main
local v = (this[lang] or {})[category]
if not v
then return this[this.main][category][key] or ''
else return v[key] or ''
end
end,
}
Locale.__index = Locale
function Locale:get(cat, k, lang)
assert(cat, 'Give category')
assert(k, 'Give key')
lang = lang or self.main
return new 'Locale'
local v = (self[lang] or {})[cat]
if not v then
return self[self.main][cat][k]
else return v[k] 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