update and fix

This commit is contained in:
Secven 2021-07-05 07:21:30 +00:00
parent eb437e4fbc
commit d41932c930
15 changed files with 160 additions and 46 deletions

9
.dockerignore Normal file
View File

@ -0,0 +1,9 @@
.idea
.git
node_modules
README.md
LICENSE
.env.example
Dockerfile
.dockerignore
yarn.lock

View File

@ -5,4 +5,5 @@ ADMIN_ID = 1652565652
# Example ( BOT_CLUSTER_CORE = 2 ) or AutoScale ( BOT_CLUSTER_CORE = 0 )
BOT_CLUSTER_CORE = 0
# Mongoose databse
#MONGOOSE_URL = 'mongodb+srv://secven:[private]@cluster0.myekb.mongodb.net/secven'
MONGOOSE_URL = 'mongodb://127.0.0.1/secven'

42
Dockerfile Normal file
View File

@ -0,0 +1,42 @@
FROM node:alpine
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
RUN echo -e " \n\
fs.file-max = 51200 \n\
\n\
net.core.rmem_max = 67108864 \n\
net.core.wmem_max = 67108864 \n\
net.ipv4.ip_forward = 1 \n\
net.core.netdev_max_backlog = 250000 \n\
net.core.somaxconn = 4096 \n\
\n\
net.ipv4.tcp_syncookies = 1 \n\
net.ipv4.tcp_tw_reuse = 1 \n\
net.ipv4.tcp_tw_recycle = 0 \n\
net.ipv4.tcp_fin_timeout = 30 \n\
net.ipv4.tcp_keepalive_time = 1200 \n\
net.ipv4.tcp_max_syn_backlog = 8192 \n\
net.ipv4.tcp_max_tw_buckets = 5000 \n\
net.ipv4.tcp_fastopen = 3 \n\
net.ipv4.tcp_mem = 25600 51200 102400 \n\
net.ipv4.tcp_rmem = 4096 87380 67108864 \n\
net.ipv4.tcp_wmem = 4096 65536 67108864 \n\
net.ipv4.tcp_mtu_probing = 1 \n\
net.ipv4.tcp_congestion_control = hybla \n\
# for low-latency network, use cubic instead \n\
# net.ipv4.tcp_congestion_control = cubic \n\
" | sed -e 's/^\s\+//g' | tee -a /etc/sysctl.conf && \
mkdir -p /etc/security && \
echo -e " \n\
* soft nofile 51200 \n\
* hard nofile 51200 \n\
" | sed -e 's/^\s\+//g' | tee -a /etc/security/limits.conf
COPY . .
RUN yarn install
CMD ["yarn", "prod"]

View File

@ -1,6 +1,8 @@
# Telegraf Bot Pattern
## Telegraf Bot Pattern
### Change .env.example to .env
![example](https://i.imgur.com/5lbe3pl.png)
#### Change .env.example to .env
> Started init: `yarn install`
@ -8,12 +10,6 @@
> Mode Cluster: `yarn prod` - In Production bot
> Mode Production PM2: `yarn pm:prod` `yarn pm:stop` `yarn pm:up` // pm2 startup
> Example `.env` ( BOT_CLUSTER_CORE = 2 ) or AutoScale ( BOT_CLUSTER_CORE = 0 )
## Todo
* _Mongoose_ [Soon]
* _Encryption Mongoose_ [Soon]
* _Webhook Cluster_ [Soon]

14
ecosystem.config.js Normal file
View File

@ -0,0 +1,14 @@
module.exports = {
apps: [
{
name: 'APP-BOT',
script: './index.js',
max_memory_restart: '1000M',
wait_ready: true,
restart_delay: 5000,
env_production: {
NODE_ENV: 'production'
}
}
]
}

View File

@ -7,7 +7,10 @@
"private": false,
"scripts": {
"dev": "cross-env NODE_ENV=development nodemon index.js",
"prod": "cross-env NODE_ENV=production node index.js"
"prod": "cross-env NODE_ENV=production node index.js",
"pm:stop": "cross-env NODE_ENV=production pm2 stop ecosystem.config.js",
"pm:prod": "cross-env NODE_ENV=production pm2 start ecosystem.config.js",
"pm:up": "pm2 startup"
},
"dependencies": {
"chalk": "^4.1.1",
@ -16,7 +19,7 @@
"mongoose": "^5.13.1",
"ms": "^2.1.3",
"telegraf": "git+https://notabug.org/Secven/telegraf",
"telegraf-i18n": "^6.6.0",
"telegraf-i18n": "git+https://notabug.org/Secven/telegraf-i18n.git",
"telegraf-middleware-console-time": "^2.1.0"
},
"devDependencies": {

View File

@ -39,7 +39,7 @@ class App {
`****** Creating instances [ ${cS.green.bold(setWorker)} ] ******`
)
for (let i = 0; i < setWorker; i += 1) {
cluster.schedulingPolicy = cluster.SCHED_RR
cluster.schedulingPolicy = cluster.SCHED_NONE
const worker = cluster.fork()
_workers.push(worker)
}
@ -49,9 +49,7 @@ class App {
console.log(cS.red.bold(`Worker ${worker.process.pid} died`))
})
process.on('uncaughtException', err => {
console.error('Cluster crashed:', err.message)
})
console.log(cS.green.bold('****** Mode: Started to Production ******'))
} else {
console.info(cS.blue(`[ Worker to ${process.pid} started ]`))
process.on('message', update => {
@ -61,9 +59,7 @@ class App {
if (cluster.isMaster) {
initTelegraf.use(ctx => handlerUpdater(ctx))
const starter = initTelegraf.launch().then(() => {
console.log(cS.green.bold('****** Mode: Started to Production ******'))
})
const starter = initTelegraf.launch()
this.killGetUpdates(starter).catch(err => console.error(err.message))
}

View File

@ -10,15 +10,18 @@ config.isDev = process.env.NODE_ENV === 'development'
config.consignOpts = {
cwd: process.cwd() + '/src',
extensions: ['.js', '.json'],
extensions: ['.js'],
verbose: false
}
config.limitSecurity = {
window: ms('15s'),
limit: 6,
onLimitExceeded: async ctx =>
await ctx.replyWithHTML('<b>❗Please wait a second (5s)</b>')
onLimitExceeded: ({ message, i18n, replyWithHTML }) =>
replyWithHTML(i18n.t('default.rateLimit'), {
reply_to_message_id: message.message_id,
disable_notification: true
})
}
config.i18nOpts = {
@ -27,6 +30,21 @@ config.i18nOpts = {
defaultLanguageOnMissing: true
}
config.settings = {
setCommand: async ctx => {
ctx.setMyCommands([
{
command: 'start',
description: 'Start bot or restart'
},
{
command: 'lang',
description: 'Change language'
}
])
}
}
module.exports = {
config,
readdirSync,

View File

@ -1,16 +1,12 @@
const consign = require('consign')
const I18n = require('telegraf-i18n')
const Telegraf = require('telegraf')
const Router = require('telegraf/router')
const Markup = require('telegraf/markup')
const Extra = require('telegraf/extra')
const session = require('telegraf/session')
const { Telegraf, Router, Markup, Extra, session } = require('telegraf')
const { I18n } = require('telegraf-i18n')
const { db } = require('../db/db')
const { config } = require('./config')
const { rateLimit, reportErr, updaterUser } = require('../middle')
const {
generateUpdateMiddleware: cliUpdateTime
} = require('telegraf-middleware-console-time')
const consign = require('consign')
const bot = new Telegraf(process.env.BOT_TOKEN, {
handlerTimeout: 1
@ -30,6 +26,7 @@ const Route = new Router(({ callbackQuery }) => {
;(async () => {
if (config.isDev) {
console.log(await bot.telegram.getMe())
await bot.use(config.settings.setCommand)
// Dev handlers
bot.use(cliUpdateTime())
bot.use(Telegraf.log())
@ -45,11 +42,23 @@ bot.use(rateLimit(config.limitSecurity))
bot.use(session())
bot.use(new I18n(config.i18nOpts))
bot.use(updaterUser)
bot.use(reportErr)
bot.on('callback_query', Route)
consign(config.consignOpts).include('handlers').then('route').into(bot)
bot.use(({ reply, i18n, message }) =>
reply(
i18n.t('default.noAnswer'),
Extra.HTML()
.inReplyTo(message.message_id)
.markup(m =>
m.inlineKeyboard([
m.callbackButton(i18n.t('default.closeMessage'), 'delete')
])
)
)
)
module.exports = { bot }

View File

@ -8,4 +8,14 @@ Object.keys(__MODELS__).forEach(model => {
return (db[model.toString()] = __MODELS__[model.toString()])
})
/*
readdirSync(path.resolve(__dirname, MODEL_PATTERN)).forEach(model => {
const setModels = model.split('.')[0].toString().trim().match(DEFAULT_CHECK)
if (!setModels)
throw new Error(`Error: secure loaded ${MODEL_PATTERN}${model.trim()}`)
// eslint-disable-next-line security/detect-non-literal-require
db[setModels.toString()] = require(MODEL_PATTERN + setModels)
})
*/
module.exports = { db }

View File

@ -1,4 +1,5 @@
const mongoose = require('mongoose')
const { cS } = require('../config/config')
class Mongoose {
async mongooseConnect() {
@ -12,7 +13,13 @@ class Mongoose {
connectTimeoutMS: 30000
})
.catch(err => console.error(err.stack))
.catch(err => {
console.error(err.stack)
console.error(
cS.red.bold(`Mongoose error: ${process.env.MONGOOSE_URL}`)
)
process.exit(0)
})
process.on('SIGINT', () => {
mongoose.connection.close(() => {

View File

@ -19,10 +19,12 @@ stackError:
default:
lang: |
🤖 Language settings
├ 🇺🇸 English
└ 🇷🇺 Русский
├ 🇺🇸 English (en)
└ 🇷🇺 Русский (ru)
langDone: |
✅ Language changed successfully (${value})
noAnswer: 🤨 I do not understand you
closeMessage: ❌ Close
rateLimit: <b>❗Please wait a second (5s)</b>

View File

@ -19,8 +19,10 @@ stackError:
default:
lang: |
🤖 Языковые настройки
├ 🇺🇸 English
└ 🇷🇺 Русский
├ 🇺🇸 English (en)
└ 🇷🇺 Русский (ru)
langDone: |
✅ Язык успешно изменен (${value})
noAnswer: 🤨 Я не понимаю тебя
closeMessage: ❌ Закрыть
rateLimit: <b>❗️Пожалуйста, подождите секунд (5s)</b>

View File

@ -1,29 +1,29 @@
const { escapeHtml } = require('../util/escapeHTML')
module.exports = async (ctx, next) => {
if (!ctx.from.id) return await next()
module.exports = async ({ db, from, i18n, session }, next) => {
// eslint-disable-next-line camelcase
const { id, first_name, username, language_code } = ctx.from
await ctx.db.User.findOne({ user_id: ctx.from.id })
const { id, first_name, username, language_code } = from
if (!id) return await next()
await db.User.findOne({ user_id: id })
.then(root => {
if (!root) {
root = new ctx.db.User()
root = new db.User()
root.user_id = id
// eslint-disable-next-line camelcase
root.first_name = escapeHtml(first_name) ?? 'Unknown'
root.username = username ?? 'Unknown'
}
// eslint-disable-next-line camelcase
root.first_name = first_name
root.first_name = escapeHtml(first_name) ?? 'Unknown'
root.username = username
// eslint-disable-next-line camelcase
root.language_code = language_code
ctx.session.root = root
ctx.i18n.locale(ctx.session.root.settings.lang)
session.root = root
i18n.locale(session.root.settings.lang)
})
.finally(() => {
return next().then(async () => {
await ctx.session.root.save()
await session.root.save()
})
})
.catch(err => console.log(err.message))

5
src/util/constants.js Normal file
View File

@ -0,0 +1,5 @@
const DEFAULT_CHECK = /^[a-zA-Z0-9.\-_]{3,20}$/gi
module.exports = {
DEFAULT_CHECK
}