-- API Library -- (c) Er2 2022 -- Zlib License require 'class' require 'api.inline' local tools = require 'api.tools' local json = tools.json -- 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 class 'API' : inherits 'EventsThis' { function(this, opts) this:super() if type(opts) == 'table' then if opts.token and type(opts.token) == 'string' then this.token = opts.token end if opts.norun then this.nr = true end if not opts.noinl then this.inline = new 'APIInline' (this) end end end, loop = function(this, limit, offset, timeout, allowed) limit = tonumber(limit) or 1 offset = tonumber(offset) or 0 timeout = tonumber(timeout) or 0 this.runs = true this:emit 'ready' while this.runs do offset = this:recvUpdate(limit, offset, timeout, allowed) end end, parseArgs = tools.parseArgs, unparseArgs = tools.unparseArgs, request = function(this, ...) return tools.request(this.token, ...) end, destroy = function(this) this.runs = false end, login = function(this, token, cb) this.token = assert(token or this.token, 'Provide token!') repeat local result, ok = this:getMe() if ok and result then this.info = result end until (this.info or {}).result this.info = this.info.result this.info.name = this.info.first_name if type(cb) == 'function' then cb(this) end if not this.nr then this:loop() end end, recvUpdate = function(this, limit, offset, timeout, allowed) local update, ok = this:getUpdates(limit, offset, timeout, allowed) if not ok then -- See warning below return offset + 1 end if ok and update and update.result then for _, upd in pairs(update.result) do offset = math.max(upd.update_id + 1, offset) this:receiveUpdate(upd) end end return offset end, receiveUpdate = function(this, upd) this:emit('update', upd) if upd.message then local msg = upd.message local cmd, to = tools.fetchCmd(msg.text or '') if cmd then -- need @to bot and /cmd@bot in groups if to and to ~= this.info.username or ((msg.chat.type == 'group' or msg.chat.type == 'supergroup') and not to) then return end -- remove command -- 2 = / + @ msg.text = msg.text:sub(#cmd + #(to or '') + 2 + 1) local args = {} for v in msg.text:gmatch '%S+' do table.insert(args, v) end msg.cmd = cmd msg.args = args this:emit('command', msg) return end this:emit('message', msg) elseif upd.edited_message then this:emit('messageEdit', upd.edited_message) elseif upd.channel_post then this:emit('channelPost', upd.channel_post) elseif upd.edited_channel_post then this:emit('channelPostEdit', upd.edited_channel_post) elseif upd.poll then this:emit('poll', upd.poll) elseif upd.poll_answer then this:emit('pollAnswer', upd.poll_answer) elseif upd.callback_query then this:emit('callbackQuery', upd.callback_query) elseif upd.inline_query then this:emit('inlineQuery', upd.inline_query) elseif upd.shipping_query then this:emit('shippingQuery', upd.shipping_query) elseif upd.pre_checkout_query then this:emit('preCheckoutQuery', upd.pre_checkout_query) elseif upd.chosen_inline_result then this:emit('inlineResult', upd.chosen_inline_result) end end, -- Getters without params getMe = function(this) return this:request 'getMe' end, getMyCommands = function(this) return this:request 'getMyCommands' end, -- Getters with params getChat = function(this, chatId) return this:request('getChat', { chat_id = chatId, }) end, getUpdates = function(this, limit, offset, timeout, allowed) -- WARNING: Due to LuaSec body limit, this function can return nil allowed = type(allowed) == 'table' and json.decode(allowed) or allowed return this:request('getUpdates', { timeout = timeout, offset = offset, limit = limit, allowed_updates = allowed, }) end, -- Setters send = function(this, chatId, text, parseMode, disableWeb, notNotify, replyTo, markup) chatId, markup, parseMode, disableWeb = argp(chatId, markup, parseMode, disableWeb) return this:request('sendMessage', { chat_id = chatId, text = tostring(text), parse_mode = parseMode, disable_web_page_preview = disableWeb, disable_notification = notNotify, reply_to_message_id = replyTo, reply_markup = markup, }) end, reply = function(this, message, text, parseMode, disableWeb, markup, notNotify) local _, markup, parseMode, disableWeb = argp(message, markup, parseMode, disableWeb) return this:request('sendMessage', { chat_id = message.chat.id, text = text, parse_mode = parseMode, disable_web_page_preview = disableWeb, disable_notification = notNotify, reply_to_message_id = message.message_id, reply_markup = markup, }) end, forward = function(this, message, to, notNotify) to = argp(to) return this:request('forwardMessage', { chat_id = to, from_chat_id = message.chat.id, disable_notification = notNotify, message_id = message.message_id, }) end, sendSticker = function(this, message, sticker, notNotify, markup, protect, replyTo, sendWreply) message, markup = argp(message, markup) return this:request('sendSticker', { chat_id = message, sticker = sticker, disable_notification = notNotify, protect_content = protect, reply_to_message_id = replyTo, allow_sending_without_reply = sendWreply, reply_markup = markup, }) end, sendPhoto = function(this, message, file, caption, parseMode, notNotify, replyTo, markup) message, markup, parseMode = argp(message, markup, parseMode) return this:request('sendPhoto', { chat_id = message, caption = caption, parse_mode = parseMode, disable_notification = notNotify, reply_to_message_id = replyTo, reply_markup = markup, }, {photo = file}) end, sendDocument = function(this, message, file, caption, parseMode, notNotify, replyTo, markup) message, markup, parseMode = argp(message, markup, parseMode) return this:request('sendDocument', { chat_id = message, caption = caption, parse_mode = parseMode, disable_notification = notNotify, reply_to_message_id = replyTo, reply_markup = markup, }, {document = file}) end, sendPoll = function(this, message, question, options, anon, type, manyAnswers, correct, explanation, parseMode, period, closes, notNotify, replyTo, markup) message, markup, parseMode = argp(message, markup, parseMode) options = type(opt) == 'string' and options or json.decode(options) anon = type(anon) == 'boolean' and anon or false manyAnswers = type(manyAnswers) == 'boolean' and manyAnswers or false return this:request('sendPoll', { chat_id = message, question = question, options = options, is_anonymous = anon, type = type, allows_multiple_answers = manyAnswers, correct_option_id = correct, explanation = explanation, explanation_parse_mode = parseMode, open_period = period, close_date = closes, disable_notification = notNotify, reply_to_message_id = replyTo, reply_markup = markup, }) end, answerCallback = function(this, id, text, alert, url, caches) return this:request('answerCallbackQuery', { callback_query_id = id, text = text, show_alert = alert, url = url, cache_time = caches, }) end, setMyCommands = function(this, commands, language, scope) return this:request('setMyCommands', { commands = json.encode(commands), language_code = tostring(language), scope = scope, }) end, }