Update
Rewritten back to Lua. Using more beautiful classes. Add Arabic (lol idk why).
This commit is contained in:
parent
5a54bc1032
commit
ed6598b145
41 changed files with 762 additions and 505 deletions
4
.dockerignore
Normal file
4
.dockerignore
Normal file
|
@ -0,0 +1,4 @@
|
|||
.env
|
||||
.gitignore
|
||||
.git
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -2,4 +2,5 @@
|
|||
*~
|
||||
\#*#
|
||||
|
||||
src/*/*.lua
|
||||
.env
|
||||
|
||||
|
|
21
Dockerfile
Normal file
21
Dockerfile
Normal 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"]
|
||||
|
13
bot.rc
13
bot.rc
|
@ -1,13 +0,0 @@
|
|||
#!/sbin/openrc-run
|
||||
# My bot service
|
||||
|
||||
# CHANGE VALUES TO MATCH YOURS
|
||||
|
||||
name="mybot"
|
||||
description="My bot"
|
||||
command="/home/er2/comp-tg/start.sh"
|
||||
command_args=""
|
||||
command_user="er2:er2"
|
||||
command_background=true
|
||||
|
||||
pidfile="/run/${RC_SVCNAME}.pid"
|
|
@ -1,12 +1,13 @@
|
|||
return {
|
||||
token = 'atokenyanedam',
|
||||
owner = 935626920 , -- hehe
|
||||
token = os.getenv 'TOKEN',
|
||||
owner = tonumber(os.getenv 'OWNER'),
|
||||
cmds = {
|
||||
'eval',
|
||||
'reload',
|
||||
'ping',
|
||||
'rub',
|
||||
'start',
|
||||
'shell',
|
||||
},
|
||||
events = {
|
||||
'command',
|
||||
|
|
2
etc/api
2
etc/api
|
@ -1 +1 @@
|
|||
Subproject commit da0a266b72f0919c5ed1bfa3fbc75e7d2f398229
|
||||
Subproject commit faab5a7e6d0dc684d04cf5121b9735b786716aca
|
116
etc/class.lua
Normal file
116
etc/class.lua
Normal 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
|
||||
|
|
@ -1,51 +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,
|
||||
class 'Events' {
|
||||
function(this)
|
||||
this._ev_ = {}
|
||||
end,
|
||||
|
||||
_add = function(this, type, name, func)
|
||||
table.insert(this._ev_, {
|
||||
type = tostring(type),
|
||||
name = tostring(name),
|
||||
func = func,
|
||||
})
|
||||
end
|
||||
end,
|
||||
|
||||
function events:on(n,f) self:_add('on', n,f) end
|
||||
function events:once(n,f) self:_add('once', n,f) end
|
||||
on = function(this, name, func)
|
||||
this:_add('on', name, func)
|
||||
end,
|
||||
|
||||
function events:off(f)
|
||||
for k, v in pairs(self._ev_) do
|
||||
if v.fn == f then
|
||||
table.remove(self._ev_, k)
|
||||
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
|
||||
end,
|
||||
|
||||
function events:_ev(t, i, name, ...)
|
||||
_ev = function(this, t, i, name, ...)
|
||||
local v = t[i]
|
||||
if v.name == name then
|
||||
v.fn(...)
|
||||
if v.type == 'once' then table.remove(t, i) end
|
||||
v.func(...)
|
||||
if v.type == 'once'
|
||||
then table.remove(t, i)
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
function events:emit(name, ...)
|
||||
local t = self._ev_
|
||||
emit = function(this, name, ...)
|
||||
local t = this._ev_
|
||||
for i = 1, #t do
|
||||
local v = t[i] or {}
|
||||
local v = t[i]
|
||||
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
|
||||
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
|
||||
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
|
||||
|
|
3
example.env
Normal file
3
example.env
Normal file
|
@ -0,0 +1,3 @@
|
|||
TOKEN= # insert bot token here
|
||||
OWNER= # insert owner ID here
|
||||
|
|
@ -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
|
29
readme.org
29
readme.org
|
@ -1,6 +1,6 @@
|
|||
* Computer bot
|
||||
|
||||
Original is *[[https://gitdab.com/er2/comp-tg][here]]* and on my private server.
|
||||
Original is *[[https://gitdab.com/er2/comp-tg][here]]* and [[https://git.er2.tech/er2/comp-tg][here]].
|
||||
Mirrors can update irregularly.
|
||||
|
||||
-----
|
||||
|
@ -15,36 +15,19 @@ and use it or fallback to English.
|
|||
Bot uses an OOP-style of Lua
|
||||
as [[https://is.gd/f0Vadk][described on Wikipedia]].
|
||||
|
||||
For more readability bot's userland written in MoonScript.
|
||||
|
||||
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 doas git lua5.3-dev luarocks musl-dev gcc openssl-dev~
|
||||
|
||||
+ Install LuaSec for https requests: ~luarocks-5.3 install luasec~
|
||||
|
||||
+ Install MoonScript: ~luarocks-5.1 install moonscript~
|
||||
|
||||
+ Create user: ~adduser user~
|
||||
|
||||
setup it (add to doas) and login to this user
|
||||
|
||||
+ Clone repo: ~git clone --recursive https://gitdab.com/er2/comp-tg~
|
||||
+ Clone repository with submodules: ~git clone --recursive https://gitdab.com/er2/comp-tg~
|
||||
|
||||
and enter ~cd comp-tg~
|
||||
|
||||
+ Change token and owner in *config.lua*
|
||||
+ Build image: ~docker build -t comp-tg .~
|
||||
|
||||
+ Compile bot: ~moonc src/~
|
||||
+ Start it: ~docker run -d -e .env --name comp-tg --restart always comp-tg~
|
||||
|
||||
+ Add service ~doas cp bot.rc /etc/init.d/mybot && doas chmod +x /etc/init.d/mybot~
|
||||
|
||||
+ Configure it ~doas vi /etc/init.d/mybot~ (change user)
|
||||
|
||||
+ Start it ~doas service mybot start~
|
||||
|
|
7
run.sh
Executable file
7
run.sh
Executable file
|
@ -0,0 +1,7 @@
|
|||
#!/bin/sh
|
||||
|
||||
chown user:user -R /app 2> /dev/null
|
||||
|
||||
cd /app
|
||||
su user -c "lua init.lua"
|
||||
|
60
src/cmds/eval.lua
Normal file
60
src/cmds/eval.lua
Normal file
|
@ -0,0 +1,60 @@
|
|||
local function prind(...)
|
||||
local s = ''
|
||||
for _, v in pairs {...} do
|
||||
if #s > 0
|
||||
then s = s .. '\t'
|
||||
end
|
||||
s = s .. tostring(v) or 'nil'
|
||||
end
|
||||
return s ..'\n'
|
||||
end
|
||||
|
||||
local env = {
|
||||
assert = assert,
|
||||
error = error,
|
||||
ipairs = ipairs,
|
||||
pairs = pairs,
|
||||
next = next,
|
||||
tonumber = tonumber,
|
||||
tostring = tostring,
|
||||
type = type,
|
||||
pcall = pcall,
|
||||
xpcall = xpcall,
|
||||
math = math,
|
||||
string = string,
|
||||
table = table,
|
||||
dump = dump,
|
||||
}
|
||||
|
||||
return {
|
||||
private = true,
|
||||
run = function(C, msg, owner)
|
||||
local s = ''
|
||||
local t = {
|
||||
msg = msg,
|
||||
print = function(...)
|
||||
s = s .. prind(...)
|
||||
end,
|
||||
C = owner and C or nil,
|
||||
api = owner and C.api or nil
|
||||
}
|
||||
|
||||
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)
|
||||
s = s ..'\n'.. e
|
||||
s = s:gsub(C.api.token:escp(), '<TOKEN>')
|
||||
C.api:reply(msg, s)
|
||||
end
|
||||
}
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
prind = (...) ->
|
||||
t = {...}
|
||||
s = ''
|
||||
for i = 1, #t
|
||||
if i > 1
|
||||
s ..= '\t'
|
||||
s ..= tostring t[i] or 'nil'
|
||||
s .. '\n'
|
||||
|
||||
env =
|
||||
assert: assert
|
||||
error: error
|
||||
ipairs: ipairs
|
||||
pairs: pairs
|
||||
next: next
|
||||
tonumber: tonumber
|
||||
tostring: tostring
|
||||
type: type
|
||||
pcall: pcall
|
||||
xpcall: xpcall
|
||||
|
||||
math: math
|
||||
string: string
|
||||
table: table
|
||||
|
||||
dump: dump
|
||||
|
||||
{
|
||||
private: true
|
||||
run: (msg, owner) =>
|
||||
s = ''
|
||||
t =
|
||||
msg: msg
|
||||
print: (...) -> s ..= prind ...
|
||||
|
||||
C: owner and @ or nil
|
||||
api: owner and @api or nil
|
||||
|
||||
for k, v in pairs env
|
||||
t[k] = v
|
||||
|
||||
e, err = load @api.unparseArgs(msg.args), 'eval', 'bt', t
|
||||
|
||||
xpcall (->
|
||||
error err if err
|
||||
e = tostring e! or '...'
|
||||
), (err) -> e = err
|
||||
|
||||
s ..= '\n'.. e
|
||||
s = s\gsub @api.token\escp!, '<TOKEN>'
|
||||
|
||||
@api\reply msg, s
|
||||
return
|
||||
}
|
11
src/cmds/ping.lua
Normal file
11
src/cmds/ping.lua
Normal file
|
@ -0,0 +1,11 @@
|
|||
return {
|
||||
run = function(C, msg)
|
||||
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
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
run: (msg) =>
|
||||
t = os.time!
|
||||
ps, ls = t - msg.date, t - @loaded
|
||||
lm = ls / 60
|
||||
lh = lm / 60
|
||||
ld = lh / 24
|
||||
@api\send msg, msg.loc.pat\format ps, ld, lh, lm, ls
|
||||
return
|
||||
}
|
32
src/cmds/reload.lua
Normal file
32
src/cmds/reload.lua
Normal 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
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
{
|
||||
private: true
|
||||
run: (msg) =>
|
||||
cat, sub, arg = table.unpack msg.args
|
||||
if not (cat and sub)
|
||||
return @api\reply msg, '/reload cmds ping'
|
||||
|
||||
path = "src.#{cat}.#{sub}"
|
||||
|
||||
@api\off package.loaded[path]
|
||||
package.loaded[path] = nil
|
||||
|
||||
if arg == '-off'
|
||||
@api\reply msg, 'Turned off'
|
||||
|
||||
else
|
||||
suc, m = pcall require, path
|
||||
if not suc then return @api\reply msg, "Reload failed. #{m}"
|
||||
switch cat
|
||||
when 'events' then @api\on sub, m
|
||||
when 'cmds' then @cmds[sub] = m
|
||||
else m @
|
||||
@api\reply msg, "Reloaded. #{m}"
|
||||
|
||||
return
|
||||
}
|
91
src/cmds/rub.lua
Normal file
91
src/cmds/rub.lua
Normal file
|
@ -0,0 +1,91 @@
|
|||
class 'Rub' {
|
||||
url = 'https://api.factmaven.com/xml-to-json/?xml=https://www.cbr.ru/scripts/XML_daily.asp',
|
||||
pat = '%d %s (%s) - %f ₽',
|
||||
|
||||
function(this)
|
||||
this.tools = require 'api.tools'
|
||||
end,
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
local rub = new 'Rub' ()
|
||||
|
||||
return {
|
||||
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 == 'err' then
|
||||
return self.api:reply(msg, self.locale:get('error', 'req_err', msg.l))
|
||||
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
|
||||
self.api:reply(msg, s .. msg.loc.prov)
|
||||
end
|
||||
}
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
-- It uses data from central bank of Russia
|
||||
--- and external service to get json from xml
|
||||
-- Privacy and security is unknown
|
||||
|
||||
rub =
|
||||
url: 'https://api.factmaven.com/xml-to-json/?xml=https://www.cbr.ru/scripts/XML_daily.asp'
|
||||
|
||||
tools: require 'etc.api.tools'
|
||||
|
||||
pat: '%d %s (%s) - %f ₽'
|
||||
|
||||
getPat: (val) =>
|
||||
@pat\format val.Nominal, val.Name, val.CharCode, val.Value\gsub ',', '.'
|
||||
|
||||
course: (wants) =>
|
||||
res, suc = @tools._req @url, 'GET'
|
||||
return 'err' if not suc
|
||||
res = @tools.json.decode res or '{}'
|
||||
res = res.ValCurs
|
||||
return 'err' if not res
|
||||
|
||||
table.insert res.Valute, {
|
||||
ID: 'R01000'
|
||||
NumCode: '001'
|
||||
CharCode: 'RUB'
|
||||
Nominal: 1
|
||||
Name: 'Российский рубль',
|
||||
Value: '1'
|
||||
}
|
||||
uah = table.findV res.Valute, CharCode: 'UAH'
|
||||
table.insert res.Valute, {
|
||||
ID: 'R02000'
|
||||
NumCode: '200'
|
||||
CharCode: 'SHT'
|
||||
Nominal: 1
|
||||
Name: 'Штаны'
|
||||
Value: ('%f')\format tonumber(uah.Value\gsub(',', '.'), nil) / uah.Nominal * 40
|
||||
}
|
||||
|
||||
wants = type(wants) == 'table' and wants or {}
|
||||
r, founds = {}, {}
|
||||
|
||||
if table.find wants, 'ALL'
|
||||
for _, v in pairs res.Valute
|
||||
table.insert r, @getPat v
|
||||
return r, res.Date, wants
|
||||
|
||||
for _, v in pairs res.Valute
|
||||
if table.find wants, v.CharCode
|
||||
table.insert founds, v.CharCode
|
||||
table.insert r, @getPat v
|
||||
|
||||
return r, res.Date, founds
|
||||
|
||||
{
|
||||
run: (msg) =>
|
||||
wants = {'USD', 'EUR', table.unpack msg.args}
|
||||
for i = 1, #wants
|
||||
wants[i] = wants[i]\upper!
|
||||
|
||||
v, d, f = rub\course wants
|
||||
if v == 'err'
|
||||
return @api\reply msg, @locale\get 'error', 'req_err', msg.l
|
||||
|
||||
nf = {}
|
||||
for _, i in pairs wants
|
||||
table.insert nf, i if not table.find f, i
|
||||
|
||||
s = msg.loc.cur\format d, table.concat v, '\n'
|
||||
if #nf > 0
|
||||
s = s .. msg.loc.notf.. table.concat nf, ','
|
||||
|
||||
@api\reply msg, s.. msg.loc.prov
|
||||
return
|
||||
}
|
16
src/cmds/shell.lua
Normal file
16
src/cmds/shell.lua
Normal 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
|
||||
}
|
||||
|
7
src/cmds/start.lua
Normal file
7
src/cmds/start.lua
Normal file
|
@ -0,0 +1,7 @@
|
|||
return {
|
||||
hide = true,
|
||||
run = function(C, msg)
|
||||
C.api:reply(msg, msg.loc.msg)
|
||||
end,
|
||||
}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
hide: true
|
||||
run: (msg) =>
|
||||
@api\reply msg, msg.loc.msg
|
||||
return
|
||||
}
|
29
src/events/command.lua
Normal file
29
src/events/command.lua
Normal file
|
@ -0,0 +1,29 @@
|
|||
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 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))
|
||||
else
|
||||
if cmd.useQArgs
|
||||
then msg.args = api.parseArgs(api.unparseArgs(msg.args))
|
||||
end
|
||||
msg.loc = C.locale:get('cmds', msg.cmd, l)
|
||||
local suc, err = pcall(cmd.run, C, msg, owner)
|
||||
if not suc then
|
||||
print(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
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
(api, msg) =>
|
||||
l = msg.from.language_code
|
||||
owner = msg.from.id == @config.owner
|
||||
cmd = @cmds[msg.cmd]
|
||||
|
||||
msg.l = l
|
||||
|
||||
if not cmd
|
||||
api\send msg, @locale\get 'error', 'inv_cmd', l
|
||||
|
||||
elseif type(cmd.run) ~= 'function'
|
||||
api\send msg, @locale\get 'error', 'cmd_run', l
|
||||
|
||||
elseif cmd.private and not owner
|
||||
api\send msg, @locale\get 'error', 'adm_cmd', l
|
||||
|
||||
else
|
||||
msg.args = api.parseArgs api.unparseArgs msg.args if cmd.useQArgs
|
||||
msg.loc = @locale\get 'cmds', msg.cmd, l
|
||||
|
||||
suc, err = pcall cmd.run, @, msg, owner
|
||||
if not suc
|
||||
-- whoops
|
||||
print err
|
||||
api\forward @config.owner, msg.chat.id, msg.message_id, false
|
||||
api\send @config.owner, err
|
||||
api\reply msg, @locale\get 'error', 'not_suc', l
|
||||
return
|
10
src/events/inlineQuery.lua
Normal file
10
src/events/inlineQuery.lua
Normal file
|
@ -0,0 +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.result('photo', '2',
|
||||
'https://cdn.discordapp.com/attachments/710425649449402408/1002915343116795914/unknown.png'
|
||||
):thumb 'https://cdn.discordapp.com/attachments/710425649449402408/1002915343116795914/unknown.png',
|
||||
})
|
||||
end
|
|
@ -1,6 +0,0 @@
|
|||
(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'
|
||||
return
|
22
src/events/message.lua
Normal file
22
src/events/message.lua
Normal 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
|
|
@ -1,42 +0,0 @@
|
|||
reg = {
|
||||
{'эх+%.*', 'хуех'} -- надоели эхать
|
||||
{'мета', 'хуета'}
|
||||
{'meta', 'xueta'}
|
||||
{'цукерберг', 'цукерхуй'}
|
||||
{'zuckerberg', 'zuckerhui'}
|
||||
{'wh?atsapp?', 'вадзад'}
|
||||
{'в[ао][тсц]+апп?', 'вадзад'}
|
||||
{'tiktok', 'деградация'}
|
||||
{'ч[ую]ма', 'капитализм'}
|
||||
{'минет', 'еблет'}
|
||||
{'еблет', 'пакет'}
|
||||
{'да', 'пизда'}
|
||||
{'нет', 'минет'}
|
||||
{'че%?*', 'пиши ё, грамотей'}
|
||||
{'чё%?*', 'ничё'}
|
||||
}
|
||||
|
||||
stick = {
|
||||
{
|
||||
'AgADwAADcpO1DQ'
|
||||
'редебало'
|
||||
'CAACAgIAAx0CUY2umQACFItiHHUg6w_MPu6Vs8k76cwn4OIHNQACwAADcpO1DVbNTDlmHOWMIwQ'
|
||||
}
|
||||
}
|
||||
|
||||
(api, msg) =>
|
||||
if msg.text
|
||||
msg.text = utf8.lower ' '.. msg.text ..' '
|
||||
t = msg.text
|
||||
for _, v in pairs reg
|
||||
t = utf8.gsub t, '%s+'.. v[1] ..'%s+', ' '.. v[2] ..' '
|
||||
|
||||
api\reply msg, t if t ~= msg.text
|
||||
elseif msg.sticker
|
||||
for k, v in pairs stick
|
||||
if msg.sticker.file_unique_id == v[1]
|
||||
if math.random! <= 0.5
|
||||
api\reply msg, v[2]
|
||||
else api\sendSticker msg, v[3] --, _, _, _, msg.message_id
|
||||
|
||||
return
|
78
src/events/ready.lua
Normal file
78
src/events/ready.lua
Normal file
|
@ -0,0 +1,78 @@
|
|||
-- 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[val]
|
||||
end
|
||||
|
||||
-- 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
|
||||
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
|
||||
local c = ''
|
||||
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'
|
||||
|
||||
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
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
|
||||
table.indexOf = (t, w) ->
|
||||
i = {}
|
||||
for k, v in pairs t
|
||||
i[v] = k
|
||||
i[w]
|
||||
|
||||
table.find = (t, w) ->
|
||||
for _, v in pairs t
|
||||
return v if v == w
|
||||
|
||||
table.findV = (t, w) ->
|
||||
b = nil
|
||||
for _, v in pairs t
|
||||
for k, x in pairs w
|
||||
if x ~= v[k]
|
||||
b = 1
|
||||
break
|
||||
if b then b = nil -- continue
|
||||
else return v
|
||||
|
||||
string.escp = (s) ->
|
||||
s\gsub '[%^%$%%%(%)%.%[%]%*%+%-%?]', '%%%0'
|
||||
|
||||
export dump = (t, d) ->
|
||||
d = 0 if not tonumber(d) or d < 0
|
||||
c = ''
|
||||
for k, v in pairs t
|
||||
if type(v) == 'table'
|
||||
v = '\n'.. dump v, d + 1
|
||||
|
||||
elseif type(v) == 'userdata'
|
||||
v = '<USERDATA>'
|
||||
|
||||
c ..= ('%s%s = %s\n')\format (' ')\rep(d), k, v
|
||||
c
|
||||
|
||||
(api) =>
|
||||
@\load 'cmds'
|
||||
|
||||
for _, lang in pairs @locale.list
|
||||
a = {}
|
||||
for k, v in pairs @cmds
|
||||
if not (v.private or v.hide)
|
||||
cmd = @locale\get 'cmds', k, lang or {}
|
||||
table.insert a, {command: k,
|
||||
description: (cmd.args and cmd.args .. ' - ' or '') .. (cmd.desc or @locale\get 'cmds', 'not_des')
|
||||
}
|
||||
api\setMyCommands a, lang
|
||||
return
|
40
src/locales/ar.json
Normal file
40
src/locales/ar.json
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,6 +14,10 @@
|
|||
"args": "<code>",
|
||||
"desc": "executes code"
|
||||
},
|
||||
"shell": {
|
||||
"args": "<code>",
|
||||
"desc": "executes code in Unix shell"
|
||||
},
|
||||
"ping": {
|
||||
"desc": "ping pong",
|
||||
|
||||
|
|
|
@ -14,6 +14,10 @@
|
|||
"args": "<код>",
|
||||
"desc": "исполняет код"
|
||||
},
|
||||
"shell": {
|
||||
"args": "<код>",
|
||||
"desc": "исполняет код в Unix shell"
|
||||
},
|
||||
"ping": {
|
||||
"desc": "пинг-понг",
|
||||
|
||||
|
|
29
src/parts/client.lua
Normal file
29
src/parts/client.lua
Normal file
|
@ -0,0 +1,29 @@
|
|||
require 'etc.api'
|
||||
|
||||
return function(C)
|
||||
C.api = new 'API' {
|
||||
norun = true,
|
||||
}
|
||||
print 'Client initialization...'
|
||||
C.api._ev = function(_, t, i, name, ...)
|
||||
return C:_ev(t, i, name, C.api, ...)
|
||||
end
|
||||
|
||||
C:load 'events'
|
||||
|
||||
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 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
|
||||
end)
|
||||
end
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
tg = require 'etc.api'
|
||||
|
||||
=>
|
||||
@api = tg { norun: true }
|
||||
@cmds = {}
|
||||
|
||||
print 'Client initialization...'
|
||||
|
||||
@_ev = (ev, ...) -> @\ev ...
|
||||
@api._ev = (_, t, i, n, ...) ->
|
||||
@._ev _, t, i, n, @api, ...
|
||||
|
||||
@\load 'events'
|
||||
|
||||
@api\login @config.token, ->
|
||||
print "Logged on as @#{@api.info.username}"
|
||||
@config.token = nil
|
||||
@api\emit 'ready'
|
||||
return
|
||||
|
||||
offs, o = 0
|
||||
@api.runs = true
|
||||
@\on 'ready', ->
|
||||
while @api.runs
|
||||
@\emit 'tick'
|
||||
|
||||
o = @api\_getUpd 1, offs, 0
|
||||
offs = o and o or offs
|
||||
|
||||
@api\getUpdates 1, offs, 0
|
||||
return
|
||||
return
|
62
src/parts/core.lua
Normal file
62
src/parts/core.lua
Normal file
|
@ -0,0 +1,62 @@
|
|||
local config = require 'config'
|
||||
require 'etc.events'
|
||||
require 'class'
|
||||
|
||||
class 'Core' : inherits 'EventsThis' {
|
||||
config = config,
|
||||
loaded = 0,
|
||||
cmds = {},
|
||||
|
||||
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,
|
||||
|
||||
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
|
||||
end
|
||||
print('Loaded '.. #c ..' '.. what)
|
||||
this.loaded = os.time()
|
||||
end,
|
||||
}
|
||||
|
||||
local core = new 'Core' ()
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
config = require 'config'
|
||||
|
||||
Core =
|
||||
config: config
|
||||
loaded: 0
|
||||
|
||||
load: (what) =>
|
||||
c = config[what]
|
||||
|
||||
for i = 1, #c
|
||||
v = c[i]
|
||||
|
||||
print "Loading #{what\sub 0, -2} (#{i} / #{#c}) #{v}..."
|
||||
-- Lint
|
||||
e, a = pcall require, "src.#{what}.#{v}"
|
||||
print e, a
|
||||
if e
|
||||
switch what
|
||||
when 'events' then @api\on v, a
|
||||
when 'cmds' then @cmds[v] = a
|
||||
when 'parts' then a @
|
||||
else print 'fail'
|
||||
print "Loaded #{#c} #{what}"
|
||||
@loaded = os.time!
|
||||
|
||||
ev: (t, i, name, ...) =>
|
||||
v = t[i]
|
||||
if v.name == name
|
||||
suc, err = pcall v.fn, @, ...
|
||||
if not suc
|
||||
print "event \"#{name}\" was failed"
|
||||
print err
|
||||
table.remove t, i if v.type == 'once'
|
||||
|
||||
init: =>
|
||||
@\load 'parts'
|
||||
|
||||
export utf8 = require 'etc.utf8'
|
||||
require 'etc.utf8data'
|
||||
utf8.config =
|
||||
conversion:
|
||||
uc_lc: utf8_uc_lc,
|
||||
lc_uc: utf8_lc_uc
|
||||
utf8\init!
|
||||
|
||||
print 'Done!'
|
||||
@\emit 'ready'
|
||||
return
|
||||
|
||||
stop: =>
|
||||
@api\destroy!
|
||||
print 'Stopped'
|
||||
print "Uptime: #{os.time! - @loaded} seconds"
|
||||
return
|
||||
|
||||
require('etc.events')(Core) -- add events
|
||||
Core\init!
|
||||
return
|
36
src/parts/locale.lua
Normal file
36
src/parts/locale.lua
Normal file
|
@ -0,0 +1,36 @@
|
|||
class 'Locale' {
|
||||
__newindex = function() end,
|
||||
list = {
|
||||
'en',
|
||||
'ru',
|
||||
'ar',
|
||||
},
|
||||
main = 'en',
|
||||
|
||||
__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,
|
||||
}
|
||||
|
||||
return new 'Locale'
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
Locale =
|
||||
__newindex: -> -- ro
|
||||
|
||||
list: {
|
||||
'en'
|
||||
'ru'
|
||||
}
|
||||
main: 'en'
|
||||
|
||||
get: (cat, k, lang) =>
|
||||
assert cat, 'Give category'
|
||||
assert k, 'Give key'
|
||||
lang or= @main
|
||||
|
||||
v = (@[lang] or {})[cat]
|
||||
if not v
|
||||
@[@main][cat][k] or {}
|
||||
else v[k] or {}
|
||||
|
||||
Locale.__index = Locale
|
||||
|
||||
(C) ->
|
||||
json = require 'etc.json'
|
||||
|
||||
for i = 1, #Locale.list
|
||||
n = Locale.list[i]
|
||||
f = io.open "src/locales/#{n}.json"
|
||||
Locale[n] = json.decode f\read 'a'
|
||||
|
||||
C.locale = setmetatable {}, Locale
|
3
start.sh
3
start.sh
|
@ -1,3 +0,0 @@
|
|||
#!/bin/sh
|
||||
cd $(dirname $0)
|
||||
luajit init.lua || lua5.3 init.lua
|
Loading…
Reference in a new issue