First beta
This commit is contained in:
		
						commit
						134c1a519d
					
				
					 14 changed files with 888 additions and 0 deletions
				
			
		
							
								
								
									
										19
									
								
								LICENSE
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								LICENSE
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,19 @@ | ||||||
|  | zlib License | ||||||
|  | 
 | ||||||
|  | Copyright (c) 2020 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 | ||||||
|  | arising from the use of this software. | ||||||
|  | 
 | ||||||
|  | Permission is granted to anyone to use this software for any purpose, | ||||||
|  | including commercial applications, and to alter it and redistribute it | ||||||
|  | freely, subject to the following restrictions: | ||||||
|  | 
 | ||||||
|  | 1. The origin of this software must not be misrepresented; you must not | ||||||
|  |    claim that you wrote the original software. If you use this software | ||||||
|  |    in a product, an acknowledgement in the product documentation would be | ||||||
|  |    appreciated but is not required. | ||||||
|  | 2. Altered source versions must be plainly marked as such, and must not be | ||||||
|  |    misrepresented as being the original software. | ||||||
|  | 3. This notice may not be removed or altered from any source distribution. | ||||||
							
								
								
									
										50
									
								
								cmds/eval.lua
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								cmds/eval.lua
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,50 @@ | ||||||
|  | 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') | ||||||
|  |   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 { | ||||||
|  |   args = '<code>', | ||||||
|  |   desc = 'evaluates code', | ||||||
|  |   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(table.concat(msg.args, ' '), 'eval', 't', 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 | ||||||
|  | } | ||||||
							
								
								
									
										6
									
								
								cmds/ping.lua
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								cmds/ping.lua
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,6 @@ | ||||||
|  | return { | ||||||
|  |   desc = 'ping pong', | ||||||
|  |   run = function(C, msg) | ||||||
|  |     C.api:send(msg, 'Pong! ' .. (os.time() - msg.date) .. 's') | ||||||
|  |   end | ||||||
|  | } | ||||||
							
								
								
									
										64
									
								
								cmds/rub.lua
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								cmds/rub.lua
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,64 @@ | ||||||
|  | local rub = { | ||||||
|  |   url = 'https://api.factmaven.com/xml-to-json/?xml=' | ||||||
|  |     .. 'https://www.cbr.ru/scripts/XML_daily.asp', | ||||||
|  |   fmt = function(v, fmt) | ||||||
|  |     fmt = type(fmt) == 'string' and fmt or '%d %s = %f' | ||||||
|  |     return fmt:format(v.Nominal, v.Name, v.Value:gsub(',', '.')) | ||||||
|  |   end | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function rub:course(wants, fmt) | ||||||
|  |   local resp, succ = (require'tg.tools').requ(self.url) | ||||||
|  |   if not succ then | ||||||
|  |     return {}, '[ошибка]', {} | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   resp = resp.ValCurs | ||||||
|  | 
 | ||||||
|  |   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, self.fmt(v, fmt)) | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   local i = table.find(wants, 'RUB') | ||||||
|  |   if i then | ||||||
|  |     table.insert(founds, 'RUB') | ||||||
|  |     table.insert(r, i, self.fmt({ | ||||||
|  |       Nominal = 1, | ||||||
|  |       Name = 'Российский рубль', | ||||||
|  |       Value = '1' | ||||||
|  |     }, fmt) .. ' :D') | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   return r, resp.Date, founds | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | function rub.msg(C, 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, '%d %s - %f ₽') | ||||||
|  |   local nf = {} | ||||||
|  | 
 | ||||||
|  |   for i = 1, #wants do | ||||||
|  |     if not table.find(f, wants[i]) then | ||||||
|  |       table.insert(nf, wants[i]) | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   local s = 'Курс на ' .. d .. ':\n' .. table.concat(v, '\n') | ||||||
|  |   if #nf > 0 then s = s .. '\n\n' .. 'Не нашлось: ' .. table.concat(nf, ', ') end | ||||||
|  | 
 | ||||||
|  |   C.api:reply(msg, s .. '\nДанные от Центробанка России') | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | return { | ||||||
|  |   args = '[valute]...', | ||||||
|  |   desc = 'ruble course', | ||||||
|  |   run = rub.msg | ||||||
|  | } | ||||||
							
								
								
									
										14
									
								
								config.lua
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								config.lua
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,14 @@ | ||||||
|  | return { | ||||||
|  |   token = 'atokenyanedam', | ||||||
|  |   owner = 'Er2Official', -- hehe | ||||||
|  |   cmds = { | ||||||
|  |     'eval', | ||||||
|  |     'rub', | ||||||
|  |     'ping', | ||||||
|  |   }, | ||||||
|  |   events = { | ||||||
|  |     'command', | ||||||
|  |     'message', | ||||||
|  |     'ready', | ||||||
|  |   }, | ||||||
|  | } | ||||||
							
								
								
									
										22
									
								
								events/command.lua
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								events/command.lua
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,22 @@ | ||||||
|  | return function(C, api, msg) | ||||||
|  |   local cmd = C.cmds[msg.cmd] | ||||||
|  |   local owner = msg.from.username == C.config.owner | ||||||
|  |   if cmd == nil then | ||||||
|  |     api:send(msg, 'Invaid command provided.') | ||||||
|  | 
 | ||||||
|  |   elseif type(cmd.run) ~= 'function' then | ||||||
|  |     api:send(msg, 'Command cannot be executed.') | ||||||
|  | 
 | ||||||
|  |   elseif cmd.private and not owner then | ||||||
|  |     api:send(msg, 'You can\'t execute private commands!') | ||||||
|  | 
 | ||||||
|  |   else | ||||||
|  |     local succ, err = pcall(cmd.run, C, msg, owner) | ||||||
|  |     if not succ then | ||||||
|  |       api:reply(msg, 'Произошла ошибочка, которая была отправлена создателю') | ||||||
|  |       local cid = api:getChat('@' .. C.config.owner).id | ||||||
|  |       api:forward(cid, msg.chat.id, msg.message_id, false) | ||||||
|  |       api:send(cid, err) | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | end | ||||||
							
								
								
									
										3
									
								
								events/message.lua
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								events/message.lua
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,3 @@ | ||||||
|  | return function(C, api, msg) | ||||||
|  | --  api:reply(msg, 'хай!') | ||||||
|  | end | ||||||
							
								
								
									
										40
									
								
								events/ready.lua
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								events/ready.lua
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,40 @@ | ||||||
|  | function table.indexOf(t, w) | ||||||
|  |   local i = {} | ||||||
|  |   for k,v in pairs(t) do i[v] = k end | ||||||
|  |   return i[w] | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | function table.find(t, w) | ||||||
|  |   local i | ||||||
|  |   for k,v in pairs(t) do | ||||||
|  |     if v == w then | ||||||
|  |       i = k | ||||||
|  |       break | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  |   return i | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | 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) end | ||||||
|  |     c = c .. string.format('%s%s = %s\n', (' '):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 then | ||||||
|  |       table.insert(a, { | ||||||
|  |         command = k, | ||||||
|  |         description = (v.args and v.args .. ' - ' or '') .. v.desc or 'no description' | ||||||
|  |       }) | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  |   api:setMyCommands(a) | ||||||
|  | end | ||||||
							
								
								
									
										45
									
								
								init.lua
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								init.lua
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,45 @@ | ||||||
|  | local config = require 'config' | ||||||
|  | 
 | ||||||
|  | local Core = { | ||||||
|  |   tg = require 'tg', | ||||||
|  |   tools = tools, | ||||||
|  |   config = config, | ||||||
|  |   cmds = {}, | ||||||
|  | } | ||||||
|  | local tg = Core.tg | ||||||
|  | 
 | ||||||
|  | function Core:load(what) | ||||||
|  |   local c = config[what] | ||||||
|  |   local s = #c | ||||||
|  |   for i = 1, s do | ||||||
|  |     local v = c[i] | ||||||
|  |     print(('Loading %s (%d / %d) %s...'):format(what:sub(0, -2), i, s, v)) | ||||||
|  |     if not pcall(require, what .. '.' .. v) then print 'fail'; goto f end | ||||||
|  | 
 | ||||||
|  |     local a = require(what .. '.' .. v) | ||||||
|  |     if what == 'events' then | ||||||
|  |       self.api['on' .. v:sub(1, 1):upper() .. v:sub(2)] = function(...) | ||||||
|  |         local succ = pcall(a, self, ...) | ||||||
|  |         if not succ then print('event ' .. v .. ' was failed') end | ||||||
|  |       end | ||||||
|  |     elseif what == 'cmds' then self.cmds[v] = a | ||||||
|  |     end | ||||||
|  |     ::f:: | ||||||
|  |   end | ||||||
|  |   print(('Loaded %d %s'):format(s, what)) | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | function Core:init() | ||||||
|  |   self.api = tg(config.token) | ||||||
|  |   self.config.token = nil | ||||||
|  |    | ||||||
|  |   print('Logged on as @' .. self.api.info.username) | ||||||
|  |   print 'Client initialization...' | ||||||
|  | 
 | ||||||
|  |   self:load 'events' | ||||||
|  | 
 | ||||||
|  |   print 'Done!' | ||||||
|  |   self.api:run() | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | Core:init() | ||||||
							
								
								
									
										93
									
								
								tg/api.lua
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								tg/api.lua
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,93 @@ | ||||||
|  | -- API Library | ||||||
|  | --- (c) Er2 <er2@dismail.de> | ||||||
|  | --- Zlib License | ||||||
|  | 
 | ||||||
|  | local tools = require 'tg.tools' | ||||||
|  | local json = require 'tg.json' | ||||||
|  | local api = { | ||||||
|  |   request = function(self, ...) return tools.request(self.token, ...) end, | ||||||
|  | } | ||||||
|  | api.__index = api -- Make class | ||||||
|  | 
 | ||||||
|  | -- Getters without params | ||||||
|  | 
 | ||||||
|  | function api:getMe() return self:request 'getMe' end | ||||||
|  | function api:getMyCommands() return self:request 'getMyCommands' end | ||||||
|  | 
 | ||||||
|  | -- Getters with params | ||||||
|  | 
 | ||||||
|  | function api:getChat(cid) return self:request('getChat', {chat_id = cid}) end | ||||||
|  | 
 | ||||||
|  | -- Setters | ||||||
|  | 
 | ||||||
|  | function api:send(msg, txt, pmod, dwp, dnot, rtmid, rmp) | ||||||
|  |   rmp = type(rmp) == 'table' and json.encode(rmp) or rmp | ||||||
|  |   msg = (type(msg) == 'table' and msg.chat and msg.chat.id) and msg.chat.id or msg | ||||||
|  |   pmod = (type(pmod) == 'boolean' and pmod == true) and 'markdown' or pmod | ||||||
|  |   if dwp == nil then dwp = true end | ||||||
|  | 
 | ||||||
|  |   return self:request('sendMessage', { | ||||||
|  |     chat_id = msg, | ||||||
|  |     text = txt, | ||||||
|  |     parse_mode = pmod, | ||||||
|  |     disable_web_page_preview = dwp, | ||||||
|  |     disable_notification = dnot, | ||||||
|  |     reply_to_message_id = rtmid, | ||||||
|  |     reply_markup = rmp, | ||||||
|  |   }) | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | function api:reply(msg, txt, pmod, dwp, rmp, dnot) | ||||||
|  |   if type(msg) ~= 'table' or not msg.chat or not msg.chat.id or not msg.message_id then return false end | ||||||
|  |   rmp = type(rmp) == 'table' and json.encode(rmp) or rmp | ||||||
|  |   pmod = (type(pmod) == 'boolean' and pmod == true) and 'markdown' or pmod | ||||||
|  | 
 | ||||||
|  |   return self:request('sendMessage', { | ||||||
|  |     chat_id = msg.chat.id, | ||||||
|  |     text = txt, | ||||||
|  |     parse_mode = pmod, | ||||||
|  |     disable_web_page_preview = dwp, | ||||||
|  |     disable_notification = dnot, | ||||||
|  |     reply_to_message_id = msg.message_id, | ||||||
|  |     reply_markup = rmp, | ||||||
|  |   }) | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | function api:forward(cid, frcid, mid, dnot) | ||||||
|  |   return self:request('forwardMessage', { | ||||||
|  |     chat_id = cid, | ||||||
|  |     from_chat_id = frcid, | ||||||
|  |     disable_notification = dnot, | ||||||
|  |     message_id = mid, | ||||||
|  |   }) | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | function api:sendPoll(cid, q, opt, anon, ptype, mansw, coptid, expl, pmode, oper, cdate, closed, dnot, rtmid, rmp) | ||||||
|  |   opt = type(opt) == 'string' and opt or json.encode(opt) | ||||||
|  |   rmp = type(rmp) == 'table' and json.encode(rmp) or rmp | ||||||
|  |   anon = type(anon) == 'boolean' and anon or false | ||||||
|  |   mansw = type(mansw) == 'boolean' and mansw or false | ||||||
|  |   return self:request('sendPoll', { | ||||||
|  |     chat_id = cid, | ||||||
|  |     question = q, | ||||||
|  |     options = opt, | ||||||
|  |     is_anonymous = anon, | ||||||
|  |     type = ptype, | ||||||
|  |     allows_multiple_answers = mansw, | ||||||
|  |     correct_option_id = coptid, | ||||||
|  |     explanation = expl, | ||||||
|  |     explanation_parse_mode = pmode, | ||||||
|  |     open_period = oper, | ||||||
|  |     close_date = cdate, | ||||||
|  |     is_closed = closed, | ||||||
|  |     disable_notification = dnot, | ||||||
|  |     reply_to_message_id = rtmid, | ||||||
|  |     reply_markup = rmp, | ||||||
|  |   }) | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | function api:setMyCommands(cmds) | ||||||
|  |   return self:request('setMyCommands', { commands = json.encode(cmds) }) | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | return api | ||||||
							
								
								
									
										110
									
								
								tg/core.lua
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								tg/core.lua
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,110 @@ | ||||||
|  | -- Core file | ||||||
|  | --- (c) Er2 <er2@dismail.de> | ||||||
|  | --- Zlib License | ||||||
|  | 
 | ||||||
|  | local tools = require 'tg.tools' | ||||||
|  | local api = require 'tg.api' | ||||||
|  | api.__index = api -- Make class | ||||||
|  | 
 | ||||||
|  | -- EVENT PROTOTYPES -- | ||||||
|  | function api.onCommand(_) end | ||||||
|  | function api.onChannelPost(_) end | ||||||
|  | function api.onChannelPostEdit(_) end | ||||||
|  | function api.onMessage(_) end | ||||||
|  | function api.onMessageEdit(_) end | ||||||
|  | function api.onInlineResult(_) end | ||||||
|  | function api.onPoll(_) end | ||||||
|  | function api.onPollAnswer(_) end | ||||||
|  | function api.onReady(_) end | ||||||
|  | function api.onQuery(_) end | ||||||
|  | function api.onUpdate(_) end | ||||||
|  | 
 | ||||||
|  | -- UPDATES -- | ||||||
|  | function api:getUpdates(tout, offs, lim, allowed) | ||||||
|  |   allowed = type(allowed) == 'table' and tools.json.encode(allowed) or allowed | ||||||
|  |   return self:request('getUpdates', { | ||||||
|  |     timeout = tout, | ||||||
|  |     offset = offs, | ||||||
|  |     limit = lim, | ||||||
|  |     allowed_updates = allowed | ||||||
|  |   }) | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | local function receiveUpdate(self, update) | ||||||
|  |   if update then self:onUpdate(update) end | ||||||
|  | 
 | ||||||
|  |   if update.message then | ||||||
|  |     local txt = update.message.text | ||||||
|  |     local cmd, to = tools.fetchCmd(txt) | ||||||
|  |     if cmd and (not to or to == self.info.username) then | ||||||
|  |       local args = {} | ||||||
|  |       txt = txt:sub(#cmd + #(to or {}) + 3) | ||||||
|  |       for s in txt:gmatch '%S+' do table.insert(args, s) end | ||||||
|  | 
 | ||||||
|  |       update.message.cmd = cmd | ||||||
|  |       update.message.args = args | ||||||
|  |       return self:onCommand(update.message, update.message.chat.type) | ||||||
|  |     elseif cmd then return end | ||||||
|  | 
 | ||||||
|  |     self:onMessage(update.message, update.message.chat.type) | ||||||
|  | 
 | ||||||
|  |   elseif update.edited_message then | ||||||
|  |     self:onMessageEdit(update.edited_message, update.edited_message.chat.type) | ||||||
|  | 
 | ||||||
|  |   elseif update.channel_post then self:onChannelPost(update.channel_post) | ||||||
|  |   elseif update.edited_channel_post then self:onChannelPostEdit(update.edited_channel_post) | ||||||
|  | 
 | ||||||
|  |   elseif update.poll then self:onPoll(update.poll) | ||||||
|  |   elseif update.poll_answer then self:onPollAnswer(update.poll_answer) | ||||||
|  | 
 | ||||||
|  |   elseif update.callback_query then self:onQuery('callback', update.callback_query) | ||||||
|  |   elseif update.inline_query then self:onQuery('inline', update.inline_query) | ||||||
|  |   elseif update.shipping_query then self:onQuery('shipping', update.shipping_query) | ||||||
|  |   elseif update.pre_checkout_query then self:onQuery('preCheckout', update.pre_checkout_query) | ||||||
|  | 
 | ||||||
|  |   elseif update.chosen_inline_result then self:onInlineResult(update.chosen_inline_result) | ||||||
|  |   end | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | function api:_loop(lim, tout, offs, al) | ||||||
|  |   while true do | ||||||
|  |     local u, ok = self:getUpdates(tout, offs, lim, al) | ||||||
|  |     if not ok or not u or (u and type(u) ~= 'table') or not u.result then goto f end | ||||||
|  |     for _, v in pairs(u.result) do | ||||||
|  |       offs = v.update_id + 1 | ||||||
|  |       receiveUpdate(self, v) | ||||||
|  |     end | ||||||
|  |     ::f:: | ||||||
|  |   end | ||||||
|  |   self:getUpdates(tout, offs, lim, al) | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | -- RUN -- | ||||||
|  | function api:run(lim, tout, offs, al) | ||||||
|  |   lim = tonumber(lim) or 1 | ||||||
|  |   tout = tonumber(tout) or 0 | ||||||
|  |   offs = tonumber(offs) or 0 | ||||||
|  | 
 | ||||||
|  |   self.runs = true | ||||||
|  |   self:onReady() | ||||||
|  | 
 | ||||||
|  |   self.co = coroutine.create(api._loop) | ||||||
|  |   coroutine.resume(self.co, self, lim, tout, offs, al) | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | function api:destroy() self.runs = false end | ||||||
|  | 
 | ||||||
|  | return function(token) | ||||||
|  |   if not token or type(token) ~= 'string' then token = nil end | ||||||
|  |   local self = setmetatable({}, api) | ||||||
|  |   self.token = assert(token, 'Provide token!') | ||||||
|  | 
 | ||||||
|  |   repeat | ||||||
|  |     local b,a = self:getMe() | ||||||
|  |     if a then self.info = b end | ||||||
|  |   until (self.info or {}).result | ||||||
|  | 
 | ||||||
|  |   self.info = self.info.result | ||||||
|  |   self.info.name = self.info.first_name | ||||||
|  |   return self | ||||||
|  | end | ||||||
							
								
								
									
										1
									
								
								tg/init.lua
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tg/init.lua
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | return require 'tg.core' | ||||||
							
								
								
									
										357
									
								
								tg/json.lua
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										357
									
								
								tg/json.lua
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,357 @@ | ||||||
|  | -- JSON Library | ||||||
|  | --- (c) 2020 rxi | ||||||
|  | --- MIT License | ||||||
|  | 
 | ||||||
|  | local json = { _version = "0.1.2" } | ||||||
|  | local encode | ||||||
|  | 
 | ||||||
|  | local escape_char_map = { | ||||||
|  |   [ "\\" ] = "\\", | ||||||
|  |   [ "\"" ] = "\"", | ||||||
|  |   [ "\b" ] = "b", | ||||||
|  |   [ "\f" ] = "f", | ||||||
|  |   [ "\n" ] = "n", | ||||||
|  |   [ "\r" ] = "r", | ||||||
|  |   [ "\t" ] = "t", | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | local escape_char_map_inv = { [ "/" ] = "/" } | ||||||
|  | for k, v in pairs(escape_char_map) do | ||||||
|  |   escape_char_map_inv[v] = k | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | local function escape_char(c) | ||||||
|  |   return "\\" .. (escape_char_map[c] or string.format("u%04x", c:byte())) | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | local function encode_nil(val) | ||||||
|  |   return "null" | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | local function encode_table(val, stack) | ||||||
|  |   local res = {} | ||||||
|  |   stack = stack or {} | ||||||
|  | 
 | ||||||
|  |   -- Circular reference? | ||||||
|  |   if stack[val] then error("circular reference") end | ||||||
|  | 
 | ||||||
|  |   stack[val] = true | ||||||
|  | 
 | ||||||
|  |   if rawget(val, 1) ~= nil or next(val) == nil then | ||||||
|  |     -- Treat as array -- check keys are valid and it is not sparse | ||||||
|  |     local n = 0 | ||||||
|  |     for k in pairs(val) do | ||||||
|  |       if type(k) ~= "number" then | ||||||
|  |         error("invalid table: mixed or invalid key types") | ||||||
|  |       end | ||||||
|  |       n = n + 1 | ||||||
|  |     end | ||||||
|  |     if n ~= #val then | ||||||
|  |       error("invalid table: sparse array") | ||||||
|  |     end | ||||||
|  |     -- Encode | ||||||
|  |     for i, v in ipairs(val) do | ||||||
|  |       table.insert(res, encode(v, stack)) | ||||||
|  |     end | ||||||
|  |     stack[val] = nil | ||||||
|  |     return "[" .. table.concat(res, ",") .. "]" | ||||||
|  | 
 | ||||||
|  |   else | ||||||
|  |     -- Treat as an object | ||||||
|  |     for k, v in pairs(val) do | ||||||
|  |       if type(k) ~= "string" then | ||||||
|  |         error("invalid table: mixed or invalid key types") | ||||||
|  |       end | ||||||
|  |       table.insert(res, encode(k, stack) .. ":" .. encode(v, stack)) | ||||||
|  |     end | ||||||
|  |     stack[val] = nil | ||||||
|  |     return "{" .. table.concat(res, ",") .. "}" | ||||||
|  |   end | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | local function encode_string(val) | ||||||
|  |   return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"' | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | local function encode_number(val) | ||||||
|  |   -- Check for NaN, -inf and inf | ||||||
|  |   if val ~= val or val <= -math.huge or val >= math.huge then | ||||||
|  |     error("unexpected number value '" .. tostring(val) .. "'") | ||||||
|  |   end | ||||||
|  |   return string.format("%.14g", val) | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | local type_func_map = { | ||||||
|  |   [ "nil"     ] = encode_nil, | ||||||
|  |   [ "table"   ] = encode_table, | ||||||
|  |   [ "string"  ] = encode_string, | ||||||
|  |   [ "number"  ] = encode_number, | ||||||
|  |   [ "boolean" ] = tostring, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | encode = function(val, stack) | ||||||
|  |   local t = type(val) | ||||||
|  |   local f = type_func_map[t] | ||||||
|  |   if f then | ||||||
|  |     return f(val, stack) | ||||||
|  |   end | ||||||
|  |   error("unexpected type '" .. t .. "'") | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | function json.encode(val) | ||||||
|  |   return ( encode(val) ) | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | local parse | ||||||
|  | 
 | ||||||
|  | local function create_set(...) | ||||||
|  |   local res = {} | ||||||
|  |   for i = 1, select("#", ...) do | ||||||
|  |     res[ select(i, ...) ] = true | ||||||
|  |   end | ||||||
|  |   return res | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | local space_chars   = create_set(" ", "\t", "\r", "\n") | ||||||
|  | local delim_chars   = create_set(" ", "\t", "\r", "\n", "]", "}", ",") | ||||||
|  | local escape_chars  = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u") | ||||||
|  | local literals      = create_set("true", "false", "null") | ||||||
|  | 
 | ||||||
|  | local literal_map = { | ||||||
|  |   [ "true"  ] = true, | ||||||
|  |   [ "false" ] = false, | ||||||
|  |   [ "null"  ] = nil, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | local function next_char(str, idx, set, negate) | ||||||
|  |   for i = idx, #str do | ||||||
|  |     if set[str:sub(i, i)] ~= negate then | ||||||
|  |       return i | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  |   return #str + 1 | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | local function decode_error(str, idx, msg) | ||||||
|  |   local line_count = 1 | ||||||
|  |   local col_count = 1 | ||||||
|  |   for i = 1, idx - 1 do | ||||||
|  |     col_count = col_count + 1 | ||||||
|  |     if str:sub(i, i) == "\n" then | ||||||
|  |       line_count = line_count + 1 | ||||||
|  |       col_count = 1 | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  |   error( string.format("%s at line %d col %d", msg, line_count, col_count) ) | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | local function codepoint_to_utf8(n) | ||||||
|  |   -- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa | ||||||
|  |   local f = math.floor | ||||||
|  |   if n <= 0x7f then | ||||||
|  |     return string.char(n) | ||||||
|  |   elseif n <= 0x7ff then | ||||||
|  |     return string.char(f(n / 64) + 192, n % 64 + 128) | ||||||
|  |   elseif n <= 0xffff then | ||||||
|  |     return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128) | ||||||
|  |   elseif n <= 0x10ffff then | ||||||
|  |     return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128, | ||||||
|  |                        f(n % 4096 / 64) + 128, n % 64 + 128) | ||||||
|  |   end | ||||||
|  |   error( string.format("invalid unicode codepoint '%x'", n) ) | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | local function parse_unicode_escape(s) | ||||||
|  |   local n1 = tonumber( s:sub(1, 4),  16 ) | ||||||
|  |   local n2 = tonumber( s:sub(7, 10), 16 ) | ||||||
|  |    -- Surrogate pair? | ||||||
|  |   if n2 then | ||||||
|  |     return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000) | ||||||
|  |   else | ||||||
|  |     return codepoint_to_utf8(n1) | ||||||
|  |   end | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | local function parse_string(str, i) | ||||||
|  |   local res = "" | ||||||
|  |   local j = i + 1 | ||||||
|  |   local k = j | ||||||
|  | 
 | ||||||
|  |   while j <= #str do | ||||||
|  |     local x = str:byte(j) | ||||||
|  | 
 | ||||||
|  |     if x < 32 then | ||||||
|  |       decode_error(str, j, "control character in string") | ||||||
|  | 
 | ||||||
|  |     elseif x == 92 then -- `\`: Escape | ||||||
|  |       res = res .. str:sub(k, j - 1) | ||||||
|  |       j = j + 1 | ||||||
|  |       local c = str:sub(j, j) | ||||||
|  |       if c == "u" then | ||||||
|  |         local hex = str:match("^[dD][89aAbB]%x%x\\u%x%x%x%x", j + 1) | ||||||
|  |                  or str:match("^%x%x%x%x", j + 1) | ||||||
|  |                  or decode_error(str, j - 1, "invalid unicode escape in string") | ||||||
|  |         res = res .. parse_unicode_escape(hex) | ||||||
|  |         j = j + #hex | ||||||
|  |       else | ||||||
|  |         if not escape_chars[c] then | ||||||
|  |           decode_error(str, j - 1, "invalid escape char '" .. c .. "' in string") | ||||||
|  |         end | ||||||
|  |         res = res .. escape_char_map_inv[c] | ||||||
|  |       end | ||||||
|  |       k = j + 1 | ||||||
|  | 
 | ||||||
|  |     elseif x == 34 then -- `"`: End of string | ||||||
|  |       res = res .. str:sub(k, j - 1) | ||||||
|  |       return res, j + 1 | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     j = j + 1 | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   decode_error(str, i, "expected closing quote for string") | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | local function parse_number(str, i) | ||||||
|  |   local x = next_char(str, i, delim_chars) | ||||||
|  |   local s = str:sub(i, x - 1) | ||||||
|  |   local n = tonumber(s) | ||||||
|  |   if not n then | ||||||
|  |     decode_error(str, i, "invalid number '" .. s .. "'") | ||||||
|  |   end | ||||||
|  |   return n, x | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | local function parse_literal(str, i) | ||||||
|  |   local x = next_char(str, i, delim_chars) | ||||||
|  |   local word = str:sub(i, x - 1) | ||||||
|  |   if not literals[word] then | ||||||
|  |     decode_error(str, i, "invalid literal '" .. word .. "'") | ||||||
|  |   end | ||||||
|  |   return literal_map[word], x | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | local function parse_array(str, i) | ||||||
|  |   local res = {} | ||||||
|  |   local n = 1 | ||||||
|  |   i = i + 1 | ||||||
|  |   while 1 do | ||||||
|  |     local x | ||||||
|  |     i = next_char(str, i, space_chars, true) | ||||||
|  |     -- Empty / end of array? | ||||||
|  |     if str:sub(i, i) == "]" then | ||||||
|  |       i = i + 1 | ||||||
|  |       break | ||||||
|  |     end | ||||||
|  |     -- Read token | ||||||
|  |     x, i = parse(str, i) | ||||||
|  |     res[n] = x | ||||||
|  |     n = n + 1 | ||||||
|  |     -- Next token | ||||||
|  |     i = next_char(str, i, space_chars, true) | ||||||
|  |     local chr = str:sub(i, i) | ||||||
|  |     i = i + 1 | ||||||
|  |     if chr == "]" then break end | ||||||
|  |     if chr ~= "," then decode_error(str, i, "expected ']' or ','") end | ||||||
|  |   end | ||||||
|  |   return res, i | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | local function parse_object(str, i) | ||||||
|  |   local res = {} | ||||||
|  |   i = i + 1 | ||||||
|  |   while 1 do | ||||||
|  |     local key, val | ||||||
|  |     i = next_char(str, i, space_chars, true) | ||||||
|  |     -- Empty / end of object? | ||||||
|  |     if str:sub(i, i) == "}" then | ||||||
|  |       i = i + 1 | ||||||
|  |       break | ||||||
|  |     end | ||||||
|  |     -- Read key | ||||||
|  |     if str:sub(i, i) ~= '"' then | ||||||
|  |       decode_error(str, i, "expected string for key") | ||||||
|  |     end | ||||||
|  |     key, i = parse(str, i) | ||||||
|  |     -- Read ':' delimiter | ||||||
|  |     i = next_char(str, i, space_chars, true) | ||||||
|  |     if str:sub(i, i) ~= ":" then | ||||||
|  |       decode_error(str, i, "expected ':' after key") | ||||||
|  |     end | ||||||
|  |     i = next_char(str, i + 1, space_chars, true) | ||||||
|  |     -- Read value | ||||||
|  |     val, i = parse(str, i) | ||||||
|  |     -- Set | ||||||
|  |     res[key] = val | ||||||
|  |     -- Next token | ||||||
|  |     i = next_char(str, i, space_chars, true) | ||||||
|  |     local chr = str:sub(i, i) | ||||||
|  |     i = i + 1 | ||||||
|  |     if chr == "}" then break end | ||||||
|  |     if chr ~= "," then decode_error(str, i, "expected '}' or ','") end | ||||||
|  |   end | ||||||
|  |   return res, i | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | local char_func_map = { | ||||||
|  |   [ '"' ] = parse_string, | ||||||
|  |   [ "0" ] = parse_number, | ||||||
|  |   [ "1" ] = parse_number, | ||||||
|  |   [ "2" ] = parse_number, | ||||||
|  |   [ "3" ] = parse_number, | ||||||
|  |   [ "4" ] = parse_number, | ||||||
|  |   [ "5" ] = parse_number, | ||||||
|  |   [ "6" ] = parse_number, | ||||||
|  |   [ "7" ] = parse_number, | ||||||
|  |   [ "8" ] = parse_number, | ||||||
|  |   [ "9" ] = parse_number, | ||||||
|  |   [ "-" ] = parse_number, | ||||||
|  |   [ "t" ] = parse_literal, | ||||||
|  |   [ "f" ] = parse_literal, | ||||||
|  |   [ "n" ] = parse_literal, | ||||||
|  |   [ "[" ] = parse_array, | ||||||
|  |   [ "{" ] = parse_object, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | parse = function(str, idx) | ||||||
|  |   local chr = str:sub(idx, idx) | ||||||
|  |   local f = char_func_map[chr] | ||||||
|  |   if f then | ||||||
|  |     return f(str, idx) | ||||||
|  |   end | ||||||
|  |   decode_error(str, idx, "unexpected character '" .. chr .. "'") | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | function json.decode(str) | ||||||
|  |   if type(str) ~= "string" then | ||||||
|  |     error("expected argument of type string, got " .. type(str)) | ||||||
|  |   end | ||||||
|  |   local res, idx = parse(str, next_char(str, 1, space_chars, true)) | ||||||
|  |   idx = next_char(str, idx, space_chars, true) | ||||||
|  |   if idx <= #str then | ||||||
|  |     decode_error(str, idx, "trailing garbage") | ||||||
|  |   end | ||||||
|  |   return res | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | return json | ||||||
							
								
								
									
										64
									
								
								tg/tools.lua
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								tg/tools.lua
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,64 @@ | ||||||
|  | local tools = { | ||||||
|  |   json = require 'tg.json', | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | local json = tools.json | ||||||
|  | local https = require 'ssl.https' | ||||||
|  | local ltn12 = require 'ltn12' | ||||||
|  | 
 | ||||||
|  | function tools.fetchCmd(text) | ||||||
|  |   local cmd = text:match '/[%w_]+' | ||||||
|  |   local to = text:match '/[%w_]+(@[%w_]+)' | ||||||
|  |   if to then to = to:sub(2) end | ||||||
|  |   if cmd then cmd = cmd:sub(2) end | ||||||
|  |   return cmd, to | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | -- https://gist.github.com/liukun/f9ce7d6d14fa45fe9b924a3eed5c3d99 | ||||||
|  | function tools.urlencode(url) | ||||||
|  |   if url == nil then return end | ||||||
|  |   url = url:gsub("\n", "\r\n") | ||||||
|  |   url = url:gsub("([^%w_%- . ~])", function(c) return string.format("%%%02X", string.byte(c)) end) | ||||||
|  |   url = url:gsub(" ", "+") | ||||||
|  |   return url | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | function tools.req(url) | ||||||
|  |   local resp = {} | ||||||
|  |   local succ, res = https.request { | ||||||
|  |     url = url, | ||||||
|  |     method = 'GET', | ||||||
|  |     sink = ltn12.sink.table(resp), | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if not succ then | ||||||
|  |     print('Connection error [' .. res .. ']') | ||||||
|  |     return nil, false | ||||||
|  |   end | ||||||
|  |   return resp[1], true | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | function tools.requ(url) | ||||||
|  |   local res, succ = tools.req(url) | ||||||
|  |   res = json.decode(res or '{}') | ||||||
|  |   if not succ or not res then return {}, false end | ||||||
|  |   return res, true | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | function tools.request(token, endpoint, param) | ||||||
|  |   assert(token, 'Provide token!') | ||||||
|  |   assert(endpoint, 'Provide endpoint!') | ||||||
|  | 
 | ||||||
|  |   local params = '' | ||||||
|  |   for k, v in pairs(param or {}) do | ||||||
|  |     params = params .. '&' .. k .. '=' .. tools.urlencode(tostring(v)) | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   local url = 'https://api.telegram.org/bot' .. token .. '/' .. endpoint | ||||||
|  |   if #params > 1 then url = url .. '?' .. params:sub(2) end | ||||||
|  | 
 | ||||||
|  |   local resp = tools.requ(url) | ||||||
|  |   return resp, resp.ok or false | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | return tools | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue