From ed6598b145a1ce29970828b5e797d33db3016833 Mon Sep 17 00:00:00 2001 From: Er2 Date: Wed, 10 Aug 2022 12:51:49 +0300 Subject: [PATCH] Update Rewritten back to Lua. Using more beautiful classes. Add Arabic (lol idk why). --- .dockerignore | 4 ++ .gitignore | 3 +- Dockerfile | 21 +++++++ bot.rc | 13 ---- config.lua | 5 +- etc/api | 2 +- etc/class.lua | 116 ++++++++++++++++++++++++++++++++++++ etc/events.lua | 109 ++++++++++++++++++++------------- example.env | 3 + LICENSE => license | 2 +- readme.org | 31 +++------- run.sh | 7 +++ src/cmds/eval.lua | 60 +++++++++++++++++++ src/cmds/eval.moon | 54 ----------------- src/cmds/ping.lua | 11 ++++ src/cmds/ping.moon | 10 ---- src/cmds/reload.lua | 32 ++++++++++ src/cmds/reload.moon | 26 -------- src/cmds/rub.lua | 91 ++++++++++++++++++++++++++++ src/cmds/rub.moon | 75 ----------------------- src/cmds/shell.lua | 16 +++++ src/cmds/start.lua | 7 +++ src/cmds/start.moon | 6 -- src/events/command.lua | 29 +++++++++ src/events/command.moon | 28 --------- src/events/inlineQuery.lua | 10 ++++ src/events/inlineQuery.moon | 6 -- src/events/message.lua | 22 +++++++ src/events/message.moon | 42 ------------- src/events/ready.lua | 78 ++++++++++++++++++++++++ src/events/ready.moon | 50 ---------------- src/locales/ar.json | 40 +++++++++++++ src/locales/en.json | 4 ++ src/locales/ru.json | 4 ++ src/parts/client.lua | 29 +++++++++ src/parts/client.moon | 32 ---------- src/parts/core.lua | 62 +++++++++++++++++++ src/parts/core.moon | 58 ------------------ src/parts/locale.lua | 36 +++++++++++ src/parts/locale.moon | 30 ---------- start.sh | 3 - 41 files changed, 762 insertions(+), 505 deletions(-) create mode 100644 .dockerignore create mode 100644 Dockerfile delete mode 100755 bot.rc create mode 100644 etc/class.lua create mode 100644 example.env rename LICENSE => license (95%) create mode 100755 run.sh create mode 100644 src/cmds/eval.lua delete mode 100644 src/cmds/eval.moon create mode 100644 src/cmds/ping.lua delete mode 100644 src/cmds/ping.moon create mode 100644 src/cmds/reload.lua delete mode 100644 src/cmds/reload.moon create mode 100644 src/cmds/rub.lua delete mode 100644 src/cmds/rub.moon create mode 100644 src/cmds/shell.lua create mode 100644 src/cmds/start.lua delete mode 100644 src/cmds/start.moon create mode 100644 src/events/command.lua delete mode 100644 src/events/command.moon create mode 100644 src/events/inlineQuery.lua delete mode 100644 src/events/inlineQuery.moon create mode 100644 src/events/message.lua delete mode 100644 src/events/message.moon create mode 100644 src/events/ready.lua delete mode 100644 src/events/ready.moon create mode 100644 src/locales/ar.json create mode 100644 src/parts/client.lua delete mode 100644 src/parts/client.moon create mode 100644 src/parts/core.lua delete mode 100644 src/parts/core.moon create mode 100644 src/parts/locale.lua delete mode 100644 src/parts/locale.moon delete mode 100755 start.sh diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..25ffda4 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +.env +.gitignore +.git + diff --git a/.gitignore b/.gitignore index cb32b3d..9e97a2f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ *~ \#*# -src/*/*.lua +.env + diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..8544ef6 --- /dev/null +++ b/Dockerfile @@ -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"] + diff --git a/bot.rc b/bot.rc deleted file mode 100755 index 55f7cd1..0000000 --- a/bot.rc +++ /dev/null @@ -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" diff --git a/config.lua b/config.lua index 4bd033e..f1d8d41 100644 --- a/config.lua +++ b/config.lua @@ -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', diff --git a/etc/api b/etc/api index da0a266..faab5a7 160000 --- a/etc/api +++ b/etc/api @@ -1 +1 @@ -Subproject commit da0a266b72f0919c5ed1bfa3fbc75e7d2f398229 +Subproject commit faab5a7e6d0dc684d04cf5121b9735b786716aca diff --git a/etc/class.lua b/etc/class.lua new file mode 100644 index 0000000..44f1bc8 --- /dev/null +++ b/etc/class.lua @@ -0,0 +1,116 @@ +-- Class library +-- (c) Er2 2022 +-- 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 + diff --git a/etc/events.lua b/etc/events.lua index 28de8d0..ab29520 100644 --- a/etc/events.lua +++ b/etc/events.lua @@ -1,51 +1,74 @@ ---[[ Events library - -- (c) Er2 2021 - -- Zlib License ---]] +-- Events library +-- (c) Er2 2022 +-- 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:off(f) - for k, v in pairs(self._ev_) do - if v.fn == f then - table.remove(self._ev_, k) + on = function(this, name, func) + this:_add('on', name, func) + 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 -end + 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 + _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, -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 + 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 diff --git a/example.env b/example.env new file mode 100644 index 0000000..a98b7e7 --- /dev/null +++ b/example.env @@ -0,0 +1,3 @@ +TOKEN= # insert bot token here +OWNER= # insert owner ID here + diff --git a/LICENSE b/license similarity index 95% rename from LICENSE rename to license index e241737..888e15d 100644 --- a/LICENSE +++ b/license @@ -1,6 +1,6 @@ Zlib License -Copyright (c) 2021 Er2 +Copyright (c) 2022 Er2 This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/readme.org b/readme.org index 20b62f1..3edf019 100644 --- a/readme.org +++ b/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~ ++ 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~ - + Install MoonScript: ~luarocks-5.1 install moonscript~ ++ Build image: ~docker build -t comp-tg .~ - + Create user: ~adduser user~ ++ Start it: ~docker run -d -e .env --name comp-tg --restart always comp-tg~ - setup it (add to doas) 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* - - + Compile bot: ~moonc src/~ - - + 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~ diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..9558980 --- /dev/null +++ b/run.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +chown user:user -R /app 2> /dev/null + +cd /app +su user -c "lua init.lua" + diff --git a/src/cmds/eval.lua b/src/cmds/eval.lua new file mode 100644 index 0000000..be6c03a --- /dev/null +++ b/src/cmds/eval.lua @@ -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(), '') + C.api:reply(msg, s) + end +} + diff --git a/src/cmds/eval.moon b/src/cmds/eval.moon deleted file mode 100644 index cd89cc4..0000000 --- a/src/cmds/eval.moon +++ /dev/null @@ -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!, '' - - @api\reply msg, s - return -} diff --git a/src/cmds/ping.lua b/src/cmds/ping.lua new file mode 100644 index 0000000..6d4eb37 --- /dev/null +++ b/src/cmds/ping.lua @@ -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 +} + diff --git a/src/cmds/ping.moon b/src/cmds/ping.moon deleted file mode 100644 index ba17429..0000000 --- a/src/cmds/ping.moon +++ /dev/null @@ -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 -} diff --git a/src/cmds/reload.lua b/src/cmds/reload.lua new file mode 100644 index 0000000..f9b9eb1 --- /dev/null +++ b/src/cmds/reload.lua @@ -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 +} + diff --git a/src/cmds/reload.moon b/src/cmds/reload.moon deleted file mode 100644 index 4ff0162..0000000 --- a/src/cmds/reload.moon +++ /dev/null @@ -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 -} diff --git a/src/cmds/rub.lua b/src/cmds/rub.lua new file mode 100644 index 0000000..f10e46b --- /dev/null +++ b/src/cmds/rub.lua @@ -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 +} + diff --git a/src/cmds/rub.moon b/src/cmds/rub.moon deleted file mode 100644 index 08b08c5..0000000 --- a/src/cmds/rub.moon +++ /dev/null @@ -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 -} diff --git a/src/cmds/shell.lua b/src/cmds/shell.lua new file mode 100644 index 0000000..f110a38 --- /dev/null +++ b/src/cmds/shell.lua @@ -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 +} + diff --git a/src/cmds/start.lua b/src/cmds/start.lua new file mode 100644 index 0000000..036c133 --- /dev/null +++ b/src/cmds/start.lua @@ -0,0 +1,7 @@ +return { + hide = true, + run = function(C, msg) + C.api:reply(msg, msg.loc.msg) + end, +} + diff --git a/src/cmds/start.moon b/src/cmds/start.moon deleted file mode 100644 index d65f196..0000000 --- a/src/cmds/start.moon +++ /dev/null @@ -1,6 +0,0 @@ -{ - hide: true - run: (msg) => - @api\reply msg, msg.loc.msg - return -} diff --git a/src/events/command.lua b/src/events/command.lua new file mode 100644 index 0000000..2a2dca4 --- /dev/null +++ b/src/events/command.lua @@ -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 + diff --git a/src/events/command.moon b/src/events/command.moon deleted file mode 100644 index cc0fcf9..0000000 --- a/src/events/command.moon +++ /dev/null @@ -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 diff --git a/src/events/inlineQuery.lua b/src/events/inlineQuery.lua new file mode 100644 index 0000000..012a87a --- /dev/null +++ b/src/events/inlineQuery.lua @@ -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 diff --git a/src/events/inlineQuery.moon b/src/events/inlineQuery.moon deleted file mode 100644 index eeba63d..0000000 --- a/src/events/inlineQuery.moon +++ /dev/null @@ -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 diff --git a/src/events/message.lua b/src/events/message.lua new file mode 100644 index 0000000..26870da --- /dev/null +++ b/src/events/message.lua @@ -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 diff --git a/src/events/message.moon b/src/events/message.moon deleted file mode 100644 index fc48613..0000000 --- a/src/events/message.moon +++ /dev/null @@ -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 diff --git a/src/events/ready.lua b/src/events/ready.lua new file mode 100644 index 0000000..69fb30b --- /dev/null +++ b/src/events/ready.lua @@ -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 = '' + 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 + diff --git a/src/events/ready.moon b/src/events/ready.moon deleted file mode 100644 index e83d27f..0000000 --- a/src/events/ready.moon +++ /dev/null @@ -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 = '' - - 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 diff --git a/src/locales/ar.json b/src/locales/ar.json new file mode 100644 index 0000000..8a14e5c --- /dev/null +++ b/src/locales/ar.json @@ -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" + } + } +} diff --git a/src/locales/en.json b/src/locales/en.json index 6252761..7ddd4bf 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -14,6 +14,10 @@ "args": "", "desc": "executes code" }, + "shell": { + "args": "", + "desc": "executes code in Unix shell" + }, "ping": { "desc": "ping pong", diff --git a/src/locales/ru.json b/src/locales/ru.json index 5b65921..88fd684 100644 --- a/src/locales/ru.json +++ b/src/locales/ru.json @@ -14,6 +14,10 @@ "args": "<код>", "desc": "исполняет код" }, + "shell": { + "args": "<код>", + "desc": "исполняет код в Unix shell" + }, "ping": { "desc": "пинг-понг", diff --git a/src/parts/client.lua b/src/parts/client.lua new file mode 100644 index 0000000..0ac4642 --- /dev/null +++ b/src/parts/client.lua @@ -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 + diff --git a/src/parts/client.moon b/src/parts/client.moon deleted file mode 100644 index 19f3c2e..0000000 --- a/src/parts/client.moon +++ /dev/null @@ -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 diff --git a/src/parts/core.lua b/src/parts/core.lua new file mode 100644 index 0000000..8b5fdb1 --- /dev/null +++ b/src/parts/core.lua @@ -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' () + diff --git a/src/parts/core.moon b/src/parts/core.moon deleted file mode 100644 index 0cdbf1b..0000000 --- a/src/parts/core.moon +++ /dev/null @@ -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 diff --git a/src/parts/locale.lua b/src/parts/locale.lua new file mode 100644 index 0000000..ce6e716 --- /dev/null +++ b/src/parts/locale.lua @@ -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' + diff --git a/src/parts/locale.moon b/src/parts/locale.moon deleted file mode 100644 index c345055..0000000 --- a/src/parts/locale.moon +++ /dev/null @@ -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 diff --git a/start.sh b/start.sh deleted file mode 100755 index e8c615f..0000000 --- a/start.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -cd $(dirname $0) -luajit init.lua || lua5.3 init.lua