Desplegando App
This commit is contained in:
288
node_modules/telegraf/core/network/client.js
generated
vendored
Normal file
288
node_modules/telegraf/core/network/client.js
generated
vendored
Normal file
@@ -0,0 +1,288 @@
|
||||
const debug = require('debug')('telegraf:client')
|
||||
const crypto = require('crypto')
|
||||
const fetch = require('node-fetch').default
|
||||
const fs = require('fs')
|
||||
const https = require('https')
|
||||
const path = require('path')
|
||||
const TelegramError = require('./error')
|
||||
const MultipartStream = require('./multipart-stream')
|
||||
const { isStream } = MultipartStream
|
||||
|
||||
const WEBHOOK_BLACKLIST = [
|
||||
'getChat',
|
||||
'getChatAdministrators',
|
||||
'getChatMember',
|
||||
'getChatMembersCount',
|
||||
'getFile',
|
||||
'getFileLink',
|
||||
'getGameHighScores',
|
||||
'getMe',
|
||||
'getUserProfilePhotos',
|
||||
'getWebhookInfo',
|
||||
'exportChatInviteLink'
|
||||
]
|
||||
|
||||
const DEFAULT_EXTENSIONS = {
|
||||
audio: 'mp3',
|
||||
photo: 'jpg',
|
||||
sticker: 'webp',
|
||||
video: 'mp4',
|
||||
animation: 'mp4',
|
||||
video_note: 'mp4',
|
||||
voice: 'ogg'
|
||||
}
|
||||
|
||||
const DEFAULT_OPTIONS = {
|
||||
apiRoot: 'https://api.telegram.org',
|
||||
webhookReply: true,
|
||||
agent: new https.Agent({
|
||||
keepAlive: true,
|
||||
keepAliveMsecs: 10000
|
||||
})
|
||||
}
|
||||
|
||||
const WEBHOOK_REPLY_STUB = {
|
||||
webhook: true,
|
||||
details: 'https://core.telegram.org/bots/api#making-requests-when-getting-updates'
|
||||
}
|
||||
|
||||
function safeJSONParse (text) {
|
||||
try {
|
||||
return JSON.parse(text)
|
||||
} catch (err) {
|
||||
debug('JSON parse failed', err)
|
||||
}
|
||||
}
|
||||
|
||||
function includesMedia (payload) {
|
||||
return Object.keys(payload).some(
|
||||
(key) => {
|
||||
const value = payload[key]
|
||||
if (Array.isArray(value)) {
|
||||
return value.some(({ media }) => media && typeof media === 'object' && (media.source || media.url))
|
||||
}
|
||||
return (typeof value === 'object') && (
|
||||
value.source ||
|
||||
value.url ||
|
||||
(typeof value.media === 'object' && (value.media.source || value.media.url))
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
function buildJSONConfig (payload) {
|
||||
return Promise.resolve({
|
||||
method: 'POST',
|
||||
compress: true,
|
||||
headers: { 'content-type': 'application/json', connection: 'keep-alive' },
|
||||
body: JSON.stringify(payload)
|
||||
})
|
||||
}
|
||||
|
||||
const FORM_DATA_JSON_FIELDS = [
|
||||
'results',
|
||||
'reply_markup',
|
||||
'mask_position',
|
||||
'shipping_options',
|
||||
'errors'
|
||||
]
|
||||
|
||||
function buildFormDataConfig (payload, agent) {
|
||||
for (const field of FORM_DATA_JSON_FIELDS) {
|
||||
if (field in payload && typeof payload[field] !== 'string') {
|
||||
payload[field] = JSON.stringify(payload[field])
|
||||
}
|
||||
}
|
||||
const boundary = crypto.randomBytes(32).toString('hex')
|
||||
const formData = new MultipartStream(boundary)
|
||||
const tasks = Object.keys(payload).map((key) => attachFormValue(formData, key, payload[key], agent))
|
||||
return Promise.all(tasks).then(() => {
|
||||
return {
|
||||
method: 'POST',
|
||||
compress: true,
|
||||
headers: { 'content-type': `multipart/form-data; boundary=${boundary}`, connection: 'keep-alive' },
|
||||
body: formData
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function attachFormValue (form, id, value, agent) {
|
||||
if (!value) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
const valueType = typeof value
|
||||
if (valueType === 'string' || valueType === 'boolean' || valueType === 'number') {
|
||||
form.addPart({
|
||||
headers: { 'content-disposition': `form-data; name="${id}"` },
|
||||
body: `${value}`
|
||||
})
|
||||
return Promise.resolve()
|
||||
}
|
||||
if (id === 'thumb') {
|
||||
const attachmentId = crypto.randomBytes(16).toString('hex')
|
||||
return attachFormMedia(form, value, attachmentId, agent)
|
||||
.then(() => form.addPart({
|
||||
headers: { 'content-disposition': `form-data; name="${id}"` },
|
||||
body: `attach://${attachmentId}`
|
||||
}))
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
return Promise.all(
|
||||
value.map((item) => {
|
||||
if (typeof item.media !== 'object') {
|
||||
return Promise.resolve(item)
|
||||
}
|
||||
const attachmentId = crypto.randomBytes(16).toString('hex')
|
||||
return attachFormMedia(form, item.media, attachmentId, agent)
|
||||
.then(() => ({ ...item, media: `attach://${attachmentId}` }))
|
||||
})
|
||||
).then((items) => form.addPart({
|
||||
headers: { 'content-disposition': `form-data; name="${id}"` },
|
||||
body: JSON.stringify(items)
|
||||
}))
|
||||
}
|
||||
if (typeof value.media !== 'undefined' && typeof value.type !== 'undefined') {
|
||||
const attachmentId = crypto.randomBytes(16).toString('hex')
|
||||
return attachFormMedia(form, value.media, attachmentId, agent)
|
||||
.then(() => form.addPart({
|
||||
headers: { 'content-disposition': `form-data; name="${id}"` },
|
||||
body: JSON.stringify({
|
||||
...value,
|
||||
media: `attach://${attachmentId}`
|
||||
})
|
||||
}))
|
||||
}
|
||||
return attachFormMedia(form, value, id, agent)
|
||||
}
|
||||
|
||||
function attachFormMedia (form, media, id, agent) {
|
||||
let fileName = media.filename || `${id}.${DEFAULT_EXTENSIONS[id] || 'dat'}`
|
||||
if (media.url) {
|
||||
return fetch(media.url, { agent }).then((res) =>
|
||||
form.addPart({
|
||||
headers: { 'content-disposition': `form-data; name="${id}"; filename="${fileName}"` },
|
||||
body: res.body
|
||||
})
|
||||
)
|
||||
}
|
||||
if (media.source) {
|
||||
if (fs.existsSync(media.source)) {
|
||||
fileName = media.filename || path.basename(media.source)
|
||||
media.source = fs.createReadStream(media.source)
|
||||
}
|
||||
if (isStream(media.source) || Buffer.isBuffer(media.source)) {
|
||||
form.addPart({
|
||||
headers: { 'content-disposition': `form-data; name="${id}"; filename="${fileName}"` },
|
||||
body: media.source
|
||||
})
|
||||
}
|
||||
}
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
function isKoaResponse (response) {
|
||||
return typeof response.set === 'function' && typeof response.header === 'object'
|
||||
}
|
||||
|
||||
function answerToWebhook (response, payload = {}, options) {
|
||||
if (!includesMedia(payload)) {
|
||||
if (isKoaResponse(response)) {
|
||||
response.body = payload
|
||||
return Promise.resolve(WEBHOOK_REPLY_STUB)
|
||||
}
|
||||
if (!response.headersSent) {
|
||||
response.setHeader('content-type', 'application/json')
|
||||
}
|
||||
return new Promise((resolve) => {
|
||||
if (response.end.length === 2) {
|
||||
response.end(JSON.stringify(payload), 'utf-8')
|
||||
return resolve(WEBHOOK_REPLY_STUB)
|
||||
}
|
||||
response.end(JSON.stringify(payload), 'utf-8', () => resolve(WEBHOOK_REPLY_STUB))
|
||||
})
|
||||
}
|
||||
|
||||
return buildFormDataConfig(payload, options.agent)
|
||||
.then(({ headers, body }) => {
|
||||
if (isKoaResponse(response)) {
|
||||
Object.keys(headers).forEach(key => response.set(key, headers[key]))
|
||||
response.body = body
|
||||
return Promise.resolve(WEBHOOK_REPLY_STUB)
|
||||
}
|
||||
if (!response.headersSent) {
|
||||
Object.keys(headers).forEach(key => response.setHeader(key, headers[key]))
|
||||
}
|
||||
return new Promise((resolve) => {
|
||||
response.on('finish', () => resolve(WEBHOOK_REPLY_STUB))
|
||||
body.pipe(response)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
class ApiClient {
|
||||
constructor (token, options, webhookResponse) {
|
||||
this.token = token
|
||||
this.options = {
|
||||
...DEFAULT_OPTIONS,
|
||||
...options
|
||||
}
|
||||
if (this.options.apiRoot.startsWith('http://')) {
|
||||
this.options.agent = null
|
||||
}
|
||||
this.response = webhookResponse
|
||||
}
|
||||
|
||||
set webhookReply (enable) {
|
||||
this.options.webhookReply = enable
|
||||
}
|
||||
|
||||
get webhookReply () {
|
||||
return this.options.webhookReply
|
||||
}
|
||||
|
||||
callApi (method, data = {}) {
|
||||
const { token, options, response, responseEnd } = this
|
||||
|
||||
const payload = Object.keys(data)
|
||||
.filter((key) => typeof data[key] !== 'undefined' && data[key] !== null)
|
||||
.reduce((acc, key) => ({ ...acc, [key]: data[key] }), {})
|
||||
|
||||
if (options.webhookReply && response && !responseEnd && !WEBHOOK_BLACKLIST.includes(method)) {
|
||||
debug('Call via webhook', method, payload)
|
||||
this.responseEnd = true
|
||||
return answerToWebhook(response, { method, ...payload }, options)
|
||||
}
|
||||
|
||||
if (!token) {
|
||||
throw new TelegramError({ error_code: 401, description: 'Bot Token is required' })
|
||||
}
|
||||
|
||||
debug('HTTP call', method, payload)
|
||||
const buildConfig = includesMedia(payload)
|
||||
? buildFormDataConfig({ method, ...payload }, options.agent)
|
||||
: buildJSONConfig(payload)
|
||||
return buildConfig
|
||||
.then((config) => {
|
||||
const apiUrl = `${options.apiRoot}/bot${token}/${method}`
|
||||
config.agent = options.agent
|
||||
return fetch(apiUrl, config)
|
||||
})
|
||||
.then((res) => res.text())
|
||||
.then((text) => {
|
||||
return safeJSONParse(text) || {
|
||||
error_code: 500,
|
||||
description: 'Unsupported http response from Telegram',
|
||||
response: text
|
||||
}
|
||||
})
|
||||
.then((data) => {
|
||||
if (!data.ok) {
|
||||
debug('API call failed', data)
|
||||
throw new TelegramError(data, { method, payload })
|
||||
}
|
||||
return data.result
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ApiClient
|
||||
12
node_modules/telegraf/core/network/error.js
generated
vendored
Normal file
12
node_modules/telegraf/core/network/error.js
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
class TelegramError extends Error {
|
||||
constructor (payload = {}, on) {
|
||||
super(`${payload.error_code}: ${payload.description}`)
|
||||
this.code = payload.error_code
|
||||
this.response = payload
|
||||
this.description = payload.description
|
||||
this.parameters = payload.parameters || {}
|
||||
this.on = on || {}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = TelegramError
|
||||
37
node_modules/telegraf/core/network/multipart-stream.js
generated
vendored
Normal file
37
node_modules/telegraf/core/network/multipart-stream.js
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
const stream = require('stream')
|
||||
const { SandwichStream } = require('sandwich-stream')
|
||||
const CRNL = '\r\n'
|
||||
|
||||
class MultipartStream extends SandwichStream {
|
||||
constructor (boundary) {
|
||||
super({
|
||||
head: `--${boundary}${CRNL}`,
|
||||
tail: `${CRNL}--${boundary}--`,
|
||||
separator: `${CRNL}--${boundary}${CRNL}`
|
||||
})
|
||||
}
|
||||
|
||||
addPart (part) {
|
||||
part = part || {}
|
||||
const partStream = new stream.PassThrough()
|
||||
if (part.headers) {
|
||||
for (const key in part.headers) {
|
||||
const header = part.headers[key]
|
||||
partStream.write(`${key}:${header}${CRNL}`)
|
||||
}
|
||||
}
|
||||
partStream.write(CRNL)
|
||||
if (MultipartStream.isStream(part.body)) {
|
||||
part.body.pipe(partStream)
|
||||
} else {
|
||||
partStream.end(part.body)
|
||||
}
|
||||
this.add(partStream)
|
||||
}
|
||||
|
||||
static isStream (stream) {
|
||||
return stream && typeof stream === 'object' && typeof stream.pipe === 'function'
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MultipartStream
|
||||
39
node_modules/telegraf/core/network/webhook.js
generated
vendored
Normal file
39
node_modules/telegraf/core/network/webhook.js
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
const debug = require('debug')('telegraf:webhook')
|
||||
|
||||
module.exports = function (hookPath, updateHandler, errorHandler) {
|
||||
return (req, res, next) => {
|
||||
debug('Incoming request', req.method, req.url)
|
||||
if (req.method !== 'POST' || req.url !== hookPath) {
|
||||
if (typeof next === 'function') {
|
||||
return next()
|
||||
}
|
||||
res.statusCode = 403
|
||||
return res.end()
|
||||
}
|
||||
let body = ''
|
||||
req.on('data', (chunk) => {
|
||||
body += chunk.toString()
|
||||
})
|
||||
req.on('end', () => {
|
||||
let update = {}
|
||||
try {
|
||||
update = JSON.parse(body)
|
||||
} catch (error) {
|
||||
res.writeHead(415)
|
||||
res.end()
|
||||
return errorHandler(error)
|
||||
}
|
||||
updateHandler(update, res)
|
||||
.then(() => {
|
||||
if (!res.finished) {
|
||||
res.end()
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
debug('Webhook error', err)
|
||||
res.writeHead(500)
|
||||
res.end()
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user