add file sending, request refactor, bugfixes
This commit is contained in:
parent
81e2a7cad7
commit
a303c6d0d0
7 changed files with 236 additions and 32 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# Emacs
|
||||||
|
*~
|
||||||
|
\#*#
|
|
@ -7,7 +7,7 @@ local tools =require 'etc.api.tools'
|
||||||
local json = require 'etc.json'
|
local json = require 'etc.json'
|
||||||
local events=require 'etc.events'
|
local events=require 'etc.events'
|
||||||
local api = {
|
local api = {
|
||||||
request = function(s, ...) return tools.request(s.token, ...) end
|
request = function(s, ...) return tools.request(s.token, ...) end,
|
||||||
}
|
}
|
||||||
api.__index = api -- Make class
|
api.__index = api -- Make class
|
||||||
|
|
||||||
|
|
|
@ -7,9 +7,10 @@ local tools = {
|
||||||
json = require 'etc.json',
|
json = require 'etc.json',
|
||||||
}
|
}
|
||||||
|
|
||||||
local json = tools.json
|
local json = tools.json
|
||||||
local https = require 'ssl.https'
|
local https = require 'ssl.https'
|
||||||
local ltn12 = require 'ltn12'
|
local ltn12 = require 'ltn12'
|
||||||
|
local mp = require 'etc.multipart'
|
||||||
|
|
||||||
function tools.fetchCmd(text)
|
function tools.fetchCmd(text)
|
||||||
return
|
return
|
||||||
|
@ -17,30 +18,23 @@ function tools.fetchCmd(text)
|
||||||
text:match '/[%w_]+@([%w_]+)' -- to
|
text:match '/[%w_]+@([%w_]+)' -- to
|
||||||
end
|
end
|
||||||
|
|
||||||
-- https://gist.github.com/liukun/f9ce7d6d14fa45fe9b924a3eed5c3d99
|
function tools._req(url, meth, data, ctype)
|
||||||
function tools.urlencode(url)
|
assert(url, 'Provide URL!')
|
||||||
if url == nil then return end
|
assert(meth, 'Provide method!')
|
||||||
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, par)
|
|
||||||
if type(par) == 'table' then
|
|
||||||
url = url .. '?'
|
|
||||||
for k,v in pairs(par) do
|
|
||||||
url = url ..'&'.. k ..'='.. tools.urlencode(tostring(v))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local resp = {}
|
local resp = {}
|
||||||
local succ, res = https.request {
|
local head = {
|
||||||
url = url,
|
url = url,
|
||||||
method = 'GET',
|
method = meth,
|
||||||
sink = ltn12.sink.table(resp),
|
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
|
if not succ then
|
||||||
print('Connection error [' .. res .. ']')
|
print('Connection error [' .. res .. ']')
|
||||||
return nil, false
|
return nil, false
|
||||||
|
@ -48,22 +42,61 @@ function tools.req(url, par)
|
||||||
return resp[1], true
|
return resp[1], true
|
||||||
end
|
end
|
||||||
|
|
||||||
function tools.requ(url, par, dbg)
|
function tools.preq(url, par)
|
||||||
local res, succ = tools.req(url, par)
|
par = par or {}
|
||||||
if dbg then print(url, succ, res) end
|
|
||||||
|
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 '{}')
|
res = json.decode(res or '{}')
|
||||||
if not succ or not res then return {}, false end
|
if not succ or not res then return {}, false end
|
||||||
return res, true
|
return res, true
|
||||||
end
|
end
|
||||||
|
|
||||||
function tools.request(token, endpoint, param, dbg)
|
function tools.request(token, endpoint, param, f, dbg)
|
||||||
assert(token, 'Provide token!')
|
assert(token, 'Provide token!')
|
||||||
assert(endpoint, 'Provide endpoint!')
|
assert(endpoint, 'Provide endpoint!')
|
||||||
|
|
||||||
local url = 'https://api.telegram.org/bot' .. token .. '/' .. endpoint
|
local url = 'https://api.telegram.org/bot' ..token.. '/' ..endpoint
|
||||||
|
|
||||||
-- dbg = true
|
-- dbg = true
|
||||||
local resp = tools.requ(url, param, dbg)
|
local resp = tools.req(url, param, f, dbg)
|
||||||
return resp, resp.ok or false
|
return resp, resp.ok or false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
167
etc/multipart.lua
Normal file
167
etc/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
|
|
@ -10,8 +10,9 @@ local rub = {
|
||||||
}
|
}
|
||||||
|
|
||||||
function rub:course(wants)
|
function rub:course(wants)
|
||||||
local resp, succ = self.tools.requ(self.url)
|
local resp, succ = self.tools._req(self.url, 'GET')
|
||||||
if not succ then return 'err' end
|
if not succ then return 'err' end
|
||||||
|
resp = self.tools.json.decode(resp or '{}')
|
||||||
|
|
||||||
resp = resp.ValCurs
|
resp = resp.ValCurs
|
||||||
table.insert(resp.Valute, {
|
table.insert(resp.Valute, {
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
local cid = C.config.owner
|
local cid = C.config.owner
|
||||||
api:forward(cid, msg.chat.id, msg.message_id, false)
|
api:forward(cid, msg.chat.id, msg.message_id, false)
|
||||||
api:send(cid, err)
|
api:send(cid, err)
|
||||||
api:reply(msg, msg.locale.error.not_suc)
|
api:reply(msg, C.locale:get('error', 'not_suc', l))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"error": {
|
"error": {
|
||||||
"inv_cmd": "Указана неизвестная команда.",
|
"inv_cmd": "Указана неизвестная команда.",
|
||||||
"adm_cmd": "Вы не можете исполнять админские команды!",
|
"adm_cmd": "Вы не можете исполнять админские команды!",
|
||||||
"cmd_run": "Это команда не мжет быть выполнена сейчас.",
|
"cmd_run": "Это команда не может быть выполнена сейчас.",
|
||||||
"not_suc": "Произошла ошибка, которая уже отправлена создателю.",
|
"not_suc": "Произошла ошибка, которая уже отправлена создателю.",
|
||||||
"unk_err": "Неизвестная ошибка.",
|
"unk_err": "Неизвестная ошибка.",
|
||||||
"req_err": "Ошибка запроса."
|
"req_err": "Ошибка запроса."
|
||||||
|
|
Loading…
Reference in a new issue