move from bot
This commit is contained in:
commit
30f7203c5f
9 changed files with 749 additions and 0 deletions
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
# Emacs
|
||||
*~
|
||||
\#*#
|
||||
|
19
LICENSE
Normal file
19
LICENSE
Normal file
|
@ -0,0 +1,19 @@
|
|||
Zlib License
|
||||
|
||||
Copyright (c) 2021 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.
|
137
api.lua
Normal file
137
api.lua
Normal file
|
@ -0,0 +1,137 @@
|
|||
--[[ API Library
|
||||
-- (c) Er2 2021 <er2@dismail.de>
|
||||
-- Zlib License
|
||||
--]]
|
||||
|
||||
local tools = require 'api.tools'
|
||||
local json = require 'json'
|
||||
local events = require 'events'
|
||||
local api = {
|
||||
request = function(s, ...) return tools.request(s.token, ...) end
|
||||
}
|
||||
api.__index = api -- Make class
|
||||
events(api) -- inheritance
|
||||
|
||||
-- parse arguments
|
||||
local function argp(cid, rmp, pmod, dwp)
|
||||
return
|
||||
type(cid) == 'table' and cid.chat.id or cid,
|
||||
type(rmp) == 'table' and json.encode(rmp) or rmp,
|
||||
(type(pmod) == 'boolean' and pmod == true) and 'MarkdownV2' or pmod,
|
||||
dwp == nil and true or dwp
|
||||
end
|
||||
|
||||
-- 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)
|
||||
msg, rmp, pmod, dwp = argp(msg, rmp, pmod, dwp)
|
||||
|
||||
if txt and #txt >= 4096 then
|
||||
txt = txt:sub(0, 4092) .. '...'
|
||||
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)
|
||||
_, rmp, pmod, dwp = argp(msg, rmp, pmod, dwp)
|
||||
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:sendPhoto(cid, f, cap, pmod, dnot, rtmid, rmp)
|
||||
cid, rmp, pmod = argp(cid, rmp, pmod)
|
||||
return self:request('sendPhoto', {
|
||||
chat_id = cid,
|
||||
caption = cap,
|
||||
parse_mode = pmod,
|
||||
disable_notification = dnot,
|
||||
reply_to_message_id = rtmid,
|
||||
reply_markup = rmp,
|
||||
}, { photo = f })
|
||||
end
|
||||
|
||||
function api:sendDocument(cid, f, cap, pmod, dnot, rtmid, rmp)
|
||||
cid, rmp, pmod = argp(cid, rmp, pmod)
|
||||
return self:request('sendDocument', {
|
||||
chat_id = cid,
|
||||
caption = cap,
|
||||
parse_mode = pmod,
|
||||
disable_notification = dnot,
|
||||
reply_to_message_id = rtmid,
|
||||
reply_markup = rmp,
|
||||
}, { document = f })
|
||||
end
|
||||
|
||||
function api:sendPoll(cid, q, opt, anon, ptype, mansw, coptid, expl, pmode, oper, cdate, closed, dnot, rtmid, rmp)
|
||||
cid, rmp, pmode = argp(cid, rmp, pmode)
|
||||
opt = type(opt) == 'string' and opt or json.encode(opt)
|
||||
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:answerCallback(id, txt, alrt, url, ctime)
|
||||
return self:request('answerCallbackQuery', {
|
||||
callback_query_id = id,
|
||||
text = txt,
|
||||
show_alert = alrt,
|
||||
url = url,
|
||||
cache_time = ctime,
|
||||
})
|
||||
end
|
||||
|
||||
function api:setMyCommands(cmds)
|
||||
return self:request('setMyCommands', { commands = json.encode(cmds) })
|
||||
end
|
||||
|
||||
return api
|
133
core.lua
Normal file
133
core.lua
Normal file
|
@ -0,0 +1,133 @@
|
|||
--[[ Core file
|
||||
-- (c) Er2 2021 <er2@dismail.de>
|
||||
-- Zlib License
|
||||
--]]
|
||||
|
||||
local tools = require 'api.tools'
|
||||
local api = require 'api.api'
|
||||
api.__index = api
|
||||
|
||||
function api:_ev(t, i, name, ...)
|
||||
local v = t[i]
|
||||
if v.name == name then
|
||||
v.fn(self, ...)
|
||||
if v.type == 'once' then table.remove(t, i) end
|
||||
end
|
||||
end
|
||||
|
||||
-- UPDATES --
|
||||
function api:getUpdates(lim, offs, tout, 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.message then
|
||||
local msg = update.message
|
||||
local cmd, to = tools.fetchCmd(msg.text or '')
|
||||
if cmd and (not to or to == self.info.username) then
|
||||
-- need /cmd@bot in groups
|
||||
if (msg.chat.type == 'group' or msg.chat.type == 'supergroup')
|
||||
and not to then return end
|
||||
|
||||
local args = {}
|
||||
msg.text = msg.text:sub(#cmd + #(to or '') + 3)
|
||||
for s in msg.text:gmatch '%S+' do table.insert(args, s) end
|
||||
|
||||
msg.cmd = cmd
|
||||
msg.args = args
|
||||
|
||||
return self:emit('command', msg)
|
||||
elseif cmd then return end
|
||||
|
||||
self:emit('message', msg)
|
||||
|
||||
elseif update.edited_message then
|
||||
self:emit('messageEdit', update.edited_message)
|
||||
|
||||
elseif update.channel_post then self:emit('channelPost', update.channel_post)
|
||||
elseif update.edited_channel_post then self:emit('channelPostEdit', update.edited_channel_post)
|
||||
|
||||
elseif update.poll then self:emit('poll', update.poll)
|
||||
elseif update.poll_answer then self:emit('pollAnswer', update.poll_answer)
|
||||
|
||||
elseif update.callback_query then self:emit('callbackQuery', update.callback_query)
|
||||
elseif update.inline_query then self:emit('inlineQuery', update.inline_query)
|
||||
elseif update.shipping_query then self:emit('shippingQuery', update.shipping_query)
|
||||
elseif update.pre_checkout_query then self:emit('preCheckoutQuery', update.pre_checkout_query)
|
||||
|
||||
elseif update.chosen_inline_result then self:emit('inlineResult', update.chosen_inline_result)
|
||||
end
|
||||
end
|
||||
|
||||
function api:_getUpd(lim, offs, ...)
|
||||
local u, ok = self:getUpdates(lim, offs, ...)
|
||||
if not ok or not u or (u and type(u) ~= 'table') or not u.result then return end
|
||||
for _, v in pairs(u.result) do
|
||||
offs = v.update_id + 1
|
||||
if type(v) == 'table' then
|
||||
self:emit('update', v)
|
||||
receiveUpdate(self, v)
|
||||
end
|
||||
end
|
||||
return offs
|
||||
end
|
||||
|
||||
function api:_loop(lim, offs, ...)
|
||||
while api.runs do
|
||||
local o = self:_getUpd(lim, offs, ...)
|
||||
offs = o and o or offs
|
||||
end
|
||||
self:getUpdates(lim, offs, ...)
|
||||
end
|
||||
|
||||
-- RUN --
|
||||
function api:run(lim, offs, tout, al)
|
||||
lim = tonumber(lim) or 1
|
||||
offs = tonumber(offs) or 0
|
||||
tout = tonumber(tout) or 0
|
||||
|
||||
self.runs = true
|
||||
self:emit 'ready'
|
||||
|
||||
self.co = coroutine.create(api._loop)
|
||||
coroutine.resume(self.co, self, lim, tout, offs, al)
|
||||
end
|
||||
|
||||
function api:destroy() self.runs = false end
|
||||
|
||||
function api:login(token, thn)
|
||||
self.token = assert(token or self.token, 'Provide token!')
|
||||
|
||||
repeat
|
||||
local r, o = self:getMe()
|
||||
if o and r then self.info = r end
|
||||
until (self.info or {}).result
|
||||
|
||||
self.info = self.info.result
|
||||
self.info.name = self.info.first_name
|
||||
|
||||
if type(thn) == 'function' then thn(self) end
|
||||
|
||||
if not self.nr then self:run() end
|
||||
end
|
||||
|
||||
return function(opts)
|
||||
if not token or type(token) ~= 'string' then token = nil end
|
||||
|
||||
local self = setmetatable({}, api)
|
||||
if type(opts) == 'table' then
|
||||
if opts.token then self.token = opts.token end
|
||||
if opts.norun then self.nr = true end
|
||||
if not opts.noinl then
|
||||
self.inline = require('etc.api.inline')(self)
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
1
init.lua
Normal file
1
init.lua
Normal file
|
@ -0,0 +1 @@
|
|||
return require 'api.core'
|
156
inline.lua
Normal file
156
inline.lua
Normal file
|
@ -0,0 +1,156 @@
|
|||
--[[ Inline query library
|
||||
-- (c) Er2 2021 <er2@dismail.de>
|
||||
-- Zlib License
|
||||
--]]
|
||||
|
||||
local inline = {}
|
||||
inline.__index = inline -- Make class
|
||||
|
||||
function inline.query(id, from, q, off, ct, loc)
|
||||
return {
|
||||
id = tostring(id),
|
||||
from = from,
|
||||
query = q,
|
||||
offset = off,
|
||||
chat_type = ct,
|
||||
location = loc,
|
||||
}
|
||||
end
|
||||
|
||||
function inline.result(type, id, ...)
|
||||
type = tostring(type)
|
||||
local t = setmetatable({
|
||||
type = type,
|
||||
id = tostring(tonumber(id) or 1),
|
||||
}, inline)
|
||||
local a = {...}
|
||||
if t.type == 'article' then t.title, t.url, t.hide_url, t.description = table.unpack(a)
|
||||
|
||||
elseif t.type == 'photo' then
|
||||
t.photo_url, t.photo_width, t.photo_height, t.title, t.description,
|
||||
t.caption, t.parse_mode, t.caption_entities
|
||||
= table.unpack(a)
|
||||
|
||||
elseif t.type == 'gif' or t.type == 'mpeg4_gif' then
|
||||
local url, width, height, duration
|
||||
url, width, height, duration, t.title, t.caption, t.parse_mode, t.caption_entities
|
||||
= table.unpack(a)
|
||||
|
||||
if t.type == 'gif' then
|
||||
t.gif_url, t.gif_width, t.gif_height, t.gif_duration
|
||||
= url, width, height, duration
|
||||
else
|
||||
t.mpeg4_url, t.mpeg4_width, t.mpeg4_height, t.mpeg4_duration
|
||||
= url, width, height, duration
|
||||
end
|
||||
|
||||
elseif t.type == 'video' then
|
||||
t.video_url, t.mime_type, t.title, t.caption, t.parse_mode,
|
||||
t.caption_entities, t.video_width, t.video_height, t.video_duration, t.description
|
||||
= table.unpack(a)
|
||||
|
||||
elseif t.type == 'audio' or t.type == 'voice' then
|
||||
t.title, t.caption, t.parse_mode, t.caption_entities = table.unpack(a, 2)
|
||||
|
||||
if t.type == 'audio' then
|
||||
t.audio_url, t.performer, t.audio_duration = a[1], a[6], a[7]
|
||||
else
|
||||
t.voice_url, t.voice_duration = a[1], a[6]
|
||||
end
|
||||
|
||||
elseif t.type == 'document' then
|
||||
t.title, t.caption, t.parse_mode, t.caption_entities, t.document_url,
|
||||
t.mime_type, t.description = table.unpack(a)
|
||||
|
||||
elseif t.type == 'location' or t.type == 'venue' then
|
||||
t.latitude, t.longitude, t.title = table.unpack(a, 1, 3)
|
||||
|
||||
if t.type ~= 'venue' then
|
||||
t.horizontal_accurancy, t.live_period, t.heading, t.proximity_alert_radius
|
||||
= table.unpack(a, 4, 7)
|
||||
else
|
||||
t.address, t.foursquare_id, t.foursquare_type, t.google_place_id, t.google_place_type
|
||||
= table.unpack(a, 4, 8)
|
||||
end
|
||||
|
||||
elseif t.type == 'contact' then
|
||||
t.phone_number, t.first_name, t.last_name, t.vcard,
|
||||
t.reply_markup, t.input_message_content
|
||||
= table.unpack(a)
|
||||
|
||||
elseif t.type == 'game' then t.game_short_name = a[1]
|
||||
end
|
||||
|
||||
return t
|
||||
end
|
||||
|
||||
function inline:thumb(url, width, height, mime)
|
||||
if self.type == 'audio'
|
||||
or self.type == 'voice'
|
||||
or self.type == 'game'
|
||||
then return self end
|
||||
|
||||
self.thumb_url = tostring(url)
|
||||
|
||||
if width and height and (
|
||||
self.type == 'article'
|
||||
or self.type == 'document'
|
||||
or self.type == 'contact'
|
||||
or self.type == 'location'
|
||||
or self.type == 'venue'
|
||||
) then
|
||||
self.thumb_width = tonumber(width)
|
||||
self.thumb_height = tonumber(height)
|
||||
end
|
||||
|
||||
if mime and (
|
||||
self.type == 'gif'
|
||||
or self.type == 'mpeg4_gif'
|
||||
) then self.thumb_mime_type = mime end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function inline:keyboard(...)
|
||||
if not self.type then return self end
|
||||
local k = {}
|
||||
|
||||
for _, v in pairs {...} do
|
||||
if type(v) == 'table' then
|
||||
table.insert(k, v)
|
||||
end
|
||||
end
|
||||
self.reply_markup = k
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
-- Author itself not understands why this funciton needed
|
||||
-- so not recommends to use it
|
||||
function inline:messCont(a)
|
||||
if self.type == 'game' or self.type == 'article' then
|
||||
self.input_message_content = a
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
function inline:answer(id, res, ctime, per, noff, pmt, pmp)
|
||||
print(dump(res))
|
||||
if res.id then res = {res} end
|
||||
return self:request('answerInlineQuery', {
|
||||
inline_query_id = id,
|
||||
results = res,
|
||||
cache_time = ctime,
|
||||
is_personal = per,
|
||||
next_offset = noff,
|
||||
switch_pm_text = pmt,
|
||||
switch_pm_parameter = pmp,
|
||||
})
|
||||
end
|
||||
|
||||
return function(api)
|
||||
local self = setmetatable({
|
||||
request = function(_, ...) api:request(...) end
|
||||
}, inline)
|
||||
return self
|
||||
end
|
167
multipart.lua
Normal file
167
multipart.lua
Normal file
|
@ -0,0 +1,167 @@
|
|||
-- based on https://github.com/catwell/lua-multipart-post
|
||||
-- MIT License
|
||||
|
||||
local ltn12 = require 'ltn12'
|
||||
local mp = {
|
||||
CHARSET = 'UTF-8',
|
||||
LANGUAGE = ''
|
||||
}
|
||||
|
||||
-- https://gist.github.com/liukun/f9ce7d6d14fa45fe9b924a3eed5c3d99
|
||||
local function 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
|
||||
mp.urlencode = urlencode
|
||||
|
||||
local function fmt(p, ...)
|
||||
if select('#', ...) == 0 then
|
||||
return p
|
||||
end
|
||||
return string.format(p, ...)
|
||||
end
|
||||
|
||||
local function tprintf(t, p, ...)
|
||||
t[#t+1] = fmt(p, ...)
|
||||
end
|
||||
|
||||
local function section_header(r, k, extra)
|
||||
tprintf(r, 'content-disposition: form-data; name="%s"', k)
|
||||
if extra.filename then
|
||||
tprintf(r, '; filename="%s"', extra.filename)
|
||||
tprintf(
|
||||
r, "; filename*=%s'%s'%s",
|
||||
mp.CHARSET, mp.LANGUAGE, urlencode(extra.filename)
|
||||
)
|
||||
end
|
||||
if extra.content_type then
|
||||
tprintf(r, '\r\ncontent-type: %s', extra.content_type)
|
||||
end
|
||||
if extra.content_transfer_encoding then
|
||||
tprintf(
|
||||
r, '\r\ncontent-transfer-encoding: %s',
|
||||
extra.content_transfer_encoding
|
||||
)
|
||||
end
|
||||
tprintf(r, '\r\n\r\n')
|
||||
end
|
||||
|
||||
function mp.boundary()
|
||||
local t = {"BOUNDARY-"}
|
||||
for i=2,17 do t[i] = string.char(math.random(65, 90)) end
|
||||
t[18] = "-BOUNDARY"
|
||||
return table.concat(t)
|
||||
end
|
||||
|
||||
local function encode_header_to_table(r, k, v, boundary)
|
||||
local _t = type(v)
|
||||
|
||||
tprintf(r, "--%s\r\n", boundary)
|
||||
if _t == "string" then
|
||||
section_header(r, k, {})
|
||||
elseif _t == "table" then
|
||||
assert(v.data, "invalid input")
|
||||
local extra = {
|
||||
filename = v.filename or v.name,
|
||||
content_type = v.content_type or v.mimetype
|
||||
or "application/octet-stream",
|
||||
content_transfer_encoding = v.content_transfer_encoding
|
||||
or "binary",
|
||||
}
|
||||
section_header(r, k, extra)
|
||||
else
|
||||
error(string.format("unexpected type %s", _t))
|
||||
end
|
||||
end
|
||||
|
||||
local function encode_header_as_source(k, v, boundary, ctx)
|
||||
local r = {}
|
||||
encode_header_to_table(r, k, v, boundary, ctx)
|
||||
local s = table.concat(r)
|
||||
if ctx then
|
||||
ctx.headers_length = ctx.headers_length + #s
|
||||
end
|
||||
return ltn12.source.string(s)
|
||||
end
|
||||
|
||||
local function data_len(d)
|
||||
local _t = type(d)
|
||||
|
||||
if _t == "string" then
|
||||
return string.len(d)
|
||||
elseif _t == "table" then
|
||||
if type(d.data) == "string" then
|
||||
return string.len(d.data)
|
||||
end
|
||||
if d.len then return d.len end
|
||||
error("must provide data length for non-string datatypes")
|
||||
end
|
||||
end
|
||||
|
||||
local function content_length(t, boundary, ctx)
|
||||
local r = ctx and ctx.headers_length or 0
|
||||
for k, v in pairs(t) do
|
||||
if not ctx then
|
||||
local tmp = {}
|
||||
encode_header_to_table(tmp, k, v, boundary)
|
||||
r = r + #table.concat(tmp)
|
||||
end
|
||||
r = r + data_len(v) + 2 -- `\r\n`
|
||||
end
|
||||
return r + #boundary + 6 -- `--BOUNDARY--\r\n`
|
||||
end
|
||||
|
||||
local function get_data_src(v)
|
||||
local _t = type(v)
|
||||
if v.source then
|
||||
return v.source
|
||||
elseif _t == "string" then
|
||||
return ltn12.source.string(v)
|
||||
elseif _t == "table" then
|
||||
_t = type(v.data)
|
||||
if _t == "string" then
|
||||
return ltn12.source.string(v.data)
|
||||
elseif _t == "table" then
|
||||
return ltn12.source.table(v.data)
|
||||
elseif _t == "userdata" then
|
||||
return ltn12.source.file(v.data)
|
||||
elseif _t == "function" then
|
||||
return v.data
|
||||
end
|
||||
end
|
||||
error("invalid input")
|
||||
end
|
||||
|
||||
local function set_ltn12_blksz(sz)
|
||||
assert(type(sz) == "number", "set_ltn12_blksz expects a number")
|
||||
ltn12.BLOCKSIZE = sz
|
||||
end
|
||||
mp.set_ltn12_blksz = set_ltn12_blksz
|
||||
|
||||
local function source(t, boundary, ctx)
|
||||
local sources, n = {}, 1
|
||||
for k, v in pairs(t) do
|
||||
sources[n] = encode_header_as_source(k, v, boundary, ctx)
|
||||
sources[n+1] = get_data_src(v)
|
||||
sources[n+2] = ltn12.source.string("\r\n")
|
||||
n = n + 3
|
||||
end
|
||||
sources[n] = ltn12.source.string(string.format("--%s--\r\n", boundary))
|
||||
return ltn12.source.cat(table.unpack(sources))
|
||||
end
|
||||
mp.source = source
|
||||
|
||||
function mp.encode(t, boundary)
|
||||
boundary = boundary or mp.boundary()
|
||||
local r = {}
|
||||
assert(ltn12.pump.all(
|
||||
(source(t, boundary)),
|
||||
(ltn12.sink.table(r))
|
||||
))
|
||||
return table.concat(r), boundary
|
||||
end
|
||||
|
||||
return mp
|
29
readme.org
Normal file
29
readme.org
Normal file
|
@ -0,0 +1,29 @@
|
|||
* Telegram API
|
||||
|
||||
This is bindings of Telegram API on Lua, part of [[https://gitdab.com/er2/comp-tg][my bot]].
|
||||
|
||||
* Installation
|
||||
|
||||
We recommended to use *.gitmodules* file for that.
|
||||
To include, add to gitmodules this:
|
||||
|
||||
#+begin_src
|
||||
[submodule "tg-api"]
|
||||
path = # enter your path
|
||||
url = https://gitdab.com/er2/tg-api-lua
|
||||
|
||||
#+end_src
|
||||
|
||||
Also you need to:
|
||||
+ Install *LuaSec* for https requests.
|
||||
|
||||
+ Execute this code *before* include this API:
|
||||
|
||||
#+begin_src lua
|
||||
package.path = 'yourpath/?.lua;yourpath/?/init.lua;' .. package.path
|
||||
#+end_src
|
||||
|
||||
+ Copy to yourpath +/api+ files *event.lua* and *json.lua*
|
||||
|
||||
They can be taken from [[https://gitdab.com/er2/comp-tg/src/branch/main/etc][here]].
|
||||
|
103
tools.lua
Normal file
103
tools.lua
Normal file
|
@ -0,0 +1,103 @@
|
|||
--[[ Additional tools
|
||||
-- (c) Er2 2021 <er2@dismail.de>
|
||||
-- Zlib license
|
||||
--]]
|
||||
|
||||
local tools = {
|
||||
json = require 'json',
|
||||
}
|
||||
|
||||
local json = tools.json
|
||||
local https = require 'ssl.https'
|
||||
local ltn12 = require 'ltn12'
|
||||
local mp = require 'api.multipart'
|
||||
|
||||
function tools.fetchCmd(text)
|
||||
return
|
||||
text:match '/([%w_]+)', -- cmd
|
||||
text:match '/[%w_]+@([%w_]+)' -- to
|
||||
end
|
||||
|
||||
function tools._req(url, meth, data, ctype)
|
||||
assert(url, 'Provide URL!')
|
||||
assert(meth, 'Provide method!')
|
||||
|
||||
local resp = {}
|
||||
local head = {
|
||||
url = url,
|
||||
method = meth,
|
||||
headers = {
|
||||
['Content-Type'] = ctype,
|
||||
['Content-Length'] = #(data or ''),
|
||||
},
|
||||
source = ltn12.source.string(data),
|
||||
sink = ltn12.sink.table(resp),
|
||||
}
|
||||
|
||||
local succ, res = https.request(head)
|
||||
if not succ then
|
||||
print('Connection error [' .. res .. ']')
|
||||
return nil, false
|
||||
end
|
||||
return resp[1], true
|
||||
end
|
||||
|
||||
function tools.preq(url, par)
|
||||
par = par or {}
|
||||
|
||||
local body, bound = mp.encode(par)
|
||||
return tools._req(
|
||||
url,
|
||||
'POST',
|
||||
body,
|
||||
'multipart/form-data; boundary=' .. bound
|
||||
)
|
||||
end
|
||||
|
||||
function tools.greq(url, par, f)
|
||||
par = json.encode(par)
|
||||
return tools._req(url, 'GET', par, 'application/json')
|
||||
end
|
||||
|
||||
function tools.req(url, par, f, dbg)
|
||||
local res, succ
|
||||
par = par or {}
|
||||
|
||||
-- files
|
||||
if f and next(f) ~= nil then
|
||||
par = par or {}
|
||||
for k, v in pairs(par) do par[k] = tostring(v) end
|
||||
local ft, fn = next(f)
|
||||
local fr = io.open(fn, 'r')
|
||||
if fr then
|
||||
par[ft] = {
|
||||
filename = fn,
|
||||
data = fr:read '*a'
|
||||
}
|
||||
fr:close()
|
||||
else par[ft] = fn end
|
||||
res, succ = tools.preq(url, par)
|
||||
else -- text
|
||||
res, succ = tools.greq(url, par)
|
||||
end
|
||||
|
||||
if dbg then print(url, succ, res, par)
|
||||
-- dump(par))
|
||||
end
|
||||
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, f)
|
||||
assert(token, 'Provide token!')
|
||||
assert(endpoint, 'Provide endpoint!')
|
||||
|
||||
local url = 'https://api.telegram.org/bot' ..token.. '/' ..endpoint
|
||||
|
||||
dbg = true
|
||||
local resp = tools.req(url, param, f, dbg)
|
||||
return resp, resp.ok or false
|
||||
end
|
||||
|
||||
return tools
|
Loading…
Reference in a new issue