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 events=require 'etc.events'
|
||||
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
|
||||
|
||||
|
|
|
@ -7,9 +7,10 @@ local tools = {
|
|||
json = require 'etc.json',
|
||||
}
|
||||
|
||||
local json = tools.json
|
||||
local json = tools.json
|
||||
local https = require 'ssl.https'
|
||||
local ltn12 = require 'ltn12'
|
||||
local mp = require 'etc.multipart'
|
||||
|
||||
function tools.fetchCmd(text)
|
||||
return
|
||||
|
@ -17,30 +18,23 @@ function tools.fetchCmd(text)
|
|||
text:match '/[%w_]+@([%w_]+)' -- 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, meth, data, ctype)
|
||||
assert(url, 'Provide URL!')
|
||||
assert(meth, 'Provide method!')
|
||||
|
||||
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 succ, res = https.request {
|
||||
url = url,
|
||||
method = 'GET',
|
||||
sink = ltn12.sink.table(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
|
||||
|
@ -48,22 +42,61 @@ function tools.req(url, par)
|
|||
return resp[1], true
|
||||
end
|
||||
|
||||
function tools.requ(url, par, dbg)
|
||||
local res, succ = tools.req(url, par)
|
||||
if dbg then print(url, succ, res) 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, dbg)
|
||||
function tools.request(token, endpoint, param, f, dbg)
|
||||
assert(token, 'Provide token!')
|
||||
assert(endpoint, 'Provide endpoint!')
|
||||
|
||||
local url = 'https://api.telegram.org/bot' .. token .. '/' .. endpoint
|
||||
local url = 'https://api.telegram.org/bot' ..token.. '/' ..endpoint
|
||||
|
||||
-- dbg = true
|
||||
local resp = tools.requ(url, param, dbg)
|
||||
local resp = tools.req(url, param, f, dbg)
|
||||
return resp, resp.ok or false
|
||||
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)
|
||||
local resp, succ = self.tools.requ(self.url)
|
||||
local resp, succ = self.tools._req(self.url, 'GET')
|
||||
if not succ then return 'err' end
|
||||
resp = self.tools.json.decode(resp or '{}')
|
||||
|
||||
resp = resp.ValCurs
|
||||
table.insert(resp.Valute, {
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
local cid = C.config.owner
|
||||
api:forward(cid, msg.chat.id, msg.message_id, false)
|
||||
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
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"error": {
|
||||
"inv_cmd": "Указана неизвестная команда.",
|
||||
"adm_cmd": "Вы не можете исполнять админские команды!",
|
||||
"cmd_run": "Это команда не мжет быть выполнена сейчас.",
|
||||
"cmd_run": "Это команда не может быть выполнена сейчас.",
|
||||
"not_suc": "Произошла ошибка, которая уже отправлена создателю.",
|
||||
"unk_err": "Неизвестная ошибка.",
|
||||
"req_err": "Ошибка запроса."
|
||||
|
|
Loading…
Reference in a new issue