├── README.md
├── data.js
├── .gitignore
├── package.json
├── LICENSE.md
└── bot.js
/README.md:
--------------------------------------------------------------------------------
1 | Code of @OneQRBot in Telegram for scanning and generating QR-code easy-way.
2 |
--------------------------------------------------------------------------------
/data.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | token: 'token', // token from @BotFather
3 | mongoLink: 'link', // mongo-link from cloud.mongodb.com
4 | dev: 123456789, // developer`s id for sending him/her errors
5 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 |
6 | # Runtime data
7 | pids
8 | *.pid
9 | *.seed
10 |
11 | # Directory for instrumented libs generated by jscoverage/JSCover
12 | lib-cov
13 |
14 | # Coverage directory used by tools like istanbul
15 | coverage
16 |
17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
18 | .grunt
19 |
20 | # node-waf configuration
21 | .lock-wscript
22 |
23 | # Compiled binary addons (http://nodejs.org/api/addons.html)
24 | build/Release
25 |
26 | # Dependency directory
27 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git
28 | node_modules
29 |
30 | # Removing private data
31 | data.js
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "oneqrbot",
3 | "version": "1.0.0",
4 | "description": "oneqrbot",
5 | "main": "bot.js",
6 | "scripts": {
7 | "test": "nodemon bot.js"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/Khuzha/oneqrbot.git"
12 | },
13 | "keywords": [],
14 | "author": "",
15 | "license": "ISC",
16 | "bugs": {
17 | "url": "https://github.com/Khuzha/oneqrbot/issues"
18 | },
19 | "homepage": "https://github.com/Khuzha/oneqrbot#readme",
20 | "dependencies": {
21 | "axios": "^0.21.1",
22 | "mongodb": "^3.2.6",
23 | "nodemon": "^1.19.1",
24 | "qrcode": "^1.3.3",
25 | "telegraf": "^3.29.0",
26 | "unirest": "^0.6.0"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 |
2 | The MIT License (MIT)
3 |
4 | Copyright (c) 2019
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | SOFTWARE.
23 |
--------------------------------------------------------------------------------
/bot.js:
--------------------------------------------------------------------------------
1 | const Telegraf = require('telegraf')
2 | const mongo = require('mongodb').MongoClient
3 | const axios = require('axios')
4 | const fs = require('fs')
5 | const data = require('./data')
6 | const countries = require('./countries')
7 | const session = require('telegraf/session')
8 | const Stage = require('telegraf/stage')
9 | const Scene = require('telegraf/scenes/base')
10 | const { leave } = Stage
11 | const stage = new Stage()
12 | const bot = new Telegraf(data.token)
13 |
14 |
15 | const scanQR = new Scene('scanQR')
16 | stage.register(scanQR)
17 | const generate = new Scene('generate')
18 | stage.register(generate)
19 | const scanBarcode = new Scene('scanBarcode')
20 | stage.register(scanBarcode)
21 |
22 | mongo.connect(data.mongoLink, {useNewUrlParser: true}, (err, client) => {
23 | if (err) {
24 | sendError(err)
25 | }
26 |
27 | db = client.db('oneqrbot')
28 | bot.startPolling()
29 | })
30 |
31 |
32 | bot.use(session())
33 | bot.use(stage.middleware())
34 |
35 | bot.start((ctx) => {
36 | starter(ctx)
37 | })
38 |
39 |
40 | bot.hears('🔍 Scan QR Code', (ctx) => {
41 | ctx.scene.enter('scanQR')
42 | })
43 |
44 | bot.hears('🖊 Generate QR Code', (ctx) => {
45 | ctx.scene.enter('generate')
46 | })
47 |
48 | bot.hears('🔍 Scan Barcode', (ctx) => {
49 | ctx.scene.enter('scanBarcode')
50 | })
51 |
52 | bot.hears('📁 Source code', (ctx) => {
53 | ctx.reply(
54 | 'You can see code of this bot on GitHub. Thanks for stars!',
55 | { reply_markup: { inline_keyboard: [[{text: '🔗 GitHub', url: 'https://github.com/Khuzha/oneqrbot'}]] } }
56 | )
57 | })
58 |
59 | scanBarcode.enter((ctx) => {
60 | ctx.reply(
61 | 'I`m ready. Send a picture!',
62 | { reply_markup: { keyboard: [['⬅️ Back']], resize_keyboard: true } }
63 | )
64 | })
65 |
66 | scanBarcode.leave((ctx) => starter(ctx))
67 |
68 | scanBarcode.on('photo', async (ctx) => {
69 | ctx.replyWithChatAction('typing')
70 |
71 | const imageData = await bot.telegram.getFile(ctx.message.photo[ctx.message.photo.length - 1].file_id)
72 | const writer = fs.createWriteStream(data.imagesFolder + imageData.file_path.substr(7))
73 |
74 | axios({
75 | method: 'get',
76 | url: `https://api.telegram.org/file/bot${data.token}/${imageData.file_path}`,
77 | responseType: 'stream'
78 | })
79 | .then(async (response) => {
80 | await response.data.pipe(writer)
81 | axios({
82 | method: 'get',
83 | url: `https://zxing.org/w/decode?u=https://khuzha.tk/barcodes/${imageData.file_path.substr(7)}`
84 | })
85 | .then((barcodeData) => {
86 | const html = barcodeData.data.toString()
87 | const start = html.indexOf('
Parsed Result | ') + 31
88 | const end = html.indexOf('')
89 |
90 | ctx.reply(`Your code is ${html.substring(start, end)}. Soon I'll say you the country of origin of products. Wait please! function is in development still.`)
91 | })
92 | .catch((err) => {
93 | if (err.response.data.includes('No barcode was found in this image')) {
94 | return ctx.reply('No data found on this photo. Please try again.')
95 | }
96 | console.log(2, err)
97 | sendError(`error when sending zxing: ${err}`, ctx)
98 | })
99 | })
100 | .catch((err) => {
101 | console.log(1, err)
102 | ctx.reply('No data found on this photo. Please try again.')
103 | sendError(err, ctx)
104 | })
105 | })
106 |
107 | scanBarcode.hears('⬅️ Back', (ctx) => {ctx.scene.leave('scanBarcode')})
108 |
109 | scanBarcode.leave((ctx) => starter(ctx))
110 |
111 |
112 | scanQR.enter((ctx) => {
113 | ctx.reply(
114 | 'I`m ready. Send a picture!',
115 | { reply_markup: { keyboard: [['⬅️ Back']], resize_keyboard: true } }
116 | )
117 | })
118 |
119 | scanQR.on('photo', async (ctx) => {
120 | ctx.replyWithChatAction('typing')
121 |
122 | const imageData = await bot.telegram.getFile(ctx.message.photo[ctx.message.photo.length - 1].file_id)
123 |
124 | axios({
125 | url: `https://api.qrserver.com/v1/read-qr-code/?fileurl=https://api.telegram.org/file/bot${data.token}/${imageData.file_path}`,
126 | method: 'GET'
127 | })
128 | .then(async (response) => {
129 | if (response.data[0].symbol[0].error === null) {
130 | await ctx.reply('Scanned data:')
131 | await ctx.reply(response.data[0].symbol[0].data)
132 | } else {
133 | await ctx.reply('No data found on this picture.')
134 | }
135 |
136 | ctx.reply('You can send me other pictures or tap "⬅️ Back"')
137 |
138 | updateStat('scanning')
139 | updateUser(ctx, true)
140 | })
141 | .catch((err) => {
142 | ctx.reply('No data found on this picture.')
143 | sendError(err, ctx)
144 | })
145 | })
146 |
147 | scanQR.hears('⬅️ Back', (ctx) => {
148 | starter(ctx)
149 | ctx.scene.leave('scanQR')
150 | })
151 |
152 |
153 | generate.enter((ctx) => {
154 | ctx.reply(
155 | 'I`m ready. Send me text!',
156 | { reply_markup: { keyboard: [['⬅️ Back']], resize_keyboard: true } }
157 | )
158 | })
159 |
160 | generate.hears('⬅️ Back', (ctx) => {
161 | starter(ctx)
162 | ctx.scene.leave('generate')
163 | })
164 |
165 | generate.on('text', async (ctx) => {
166 | if (ctx.message.text.length > 900) {
167 | return ctx.reply('Your text is too long. Please send text that contains not more than 900 symbols.')
168 | }
169 |
170 | ctx.replyWithChatAction('upload_photo')
171 |
172 | axios.get(`http://api.qrserver.com/v1/create-qr-code/?data=${encodeURI(ctx.message.text)}&size=300x300`)
173 | .then(async (response) => {
174 | await ctx.replyWithPhoto(`http://api.qrserver.com/v1/create-qr-code/?data=${encodeURI(ctx.message.text)}&size=300x300`, { caption: 'Generated via @OneQRBot' })
175 | ctx.reply('You can send me another text or tap "⬅️ Back"')
176 |
177 | updateStat('generating')
178 | updateUser(ctx, true)
179 | })
180 | .catch(async (err) => {
181 | console.log(err)
182 | await ctx.reply('Data you sent isn`t valid. Please check that and try again.')
183 | ctx.reply('You can send me another text or tap "⬅️ Back"')
184 |
185 | sendError(`Generating error by message ${ctx.message.text}: \n\n ${err.toString()}`, ctx)
186 | })
187 | })
188 |
189 |
190 | bot.hears('📈 Statistic', async (ctx) => {
191 | ctx.replyWithChatAction('typing')
192 |
193 | const allUsers = (await db.collection('allUsers').find({}).toArray()).length
194 | const activeUsers = (await db.collection('allUsers').find({status: 'active'}).toArray()).length
195 | const blockedUsers = (await db.collection('allUsers').find({status: 'blocked'}).toArray()).length
196 | const scanned = await db.collection('statistic').find({genAct: 'scanning'}).toArray()
197 | const generated = await db.collection('statistic').find({genAct: 'generating'}).toArray()
198 | const button = (await db.collection('statistic').find({genAct: 'button'}).toArray())[0].count
199 | let todayScans = +(await db.collection('statistic').find({action: 'scanning'}).toArray())[0][makeDate()]
200 | let todayGens = +(await db.collection('statistic').find({action: 'generating'}).toArray())[0][makeDate()]
201 |
202 | !todayScans ? todayScans = 0 : false
203 | !todayGens ? todayGens = 0 : false
204 |
205 | let scansPercent = Math.round((scanned[0].count / (scanned[0].count + generated[0].count)) * 100)
206 | let gensPercent = Math.round((generated[0].count / (scanned[0].count + generated[0].count)) * 100)
207 | let todayScansPercent = Math.round((todayScans / (todayScans + todayGens)) * 100)
208 | let todayGensPercent = Math.round((todayGens / (todayScans + todayGens)) * 100)
209 |
210 | !scansPercent ? scansPercent = 0 : false
211 | !gensPercent ? gensPercent = 0 : false
212 | !todayScansPercent ? todayScansPercent = 0 : false
213 | !todayGensPercent ? todayGensPercent = 0 : false
214 |
215 | ctx.reply(
216 | `👥 Total users: ${allUsers}` +
217 | `\n🤴 Active users: ${activeUsers} - ${Math.round((activeUsers / allUsers) * 100)}%` +
218 | `\n🧛♂️ Blocked users: ${blockedUsers} - ${Math.round((blockedUsers / allUsers) * 100)}%` +
219 |
220 | `\n\n🕹 All actions: ${scanned[0].count + generated[0].count}` +
221 | `\n📽 Scanned: ${scanned[0].count} times - ${scansPercent}%` +
222 | `\n📤 Generated: ${generated[0].count} times - ${gensPercent}%` +
223 |
224 | `\n\n📅 Actions today: ${todayScans + todayGens} - ${Math.round((todayScans + todayGens) / (scanned[0].count + generated[0].count) * 100)}% of all` +
225 | `\n📽 Scanned today: ${todayScans} times - ${todayScansPercent}%` +
226 | `\n📤 Generated today: ${todayGens} times - ${todayGensPercent}%` +
227 |
228 | `\n\n⭕️ This button was pressed ${button} times`,
229 | {parse_mode: 'html'}
230 | )
231 |
232 | updateStat('button')
233 | })
234 |
235 |
236 | bot.command('users', async (ctx) => {
237 | let allUsers = await db.collection('allUsers').find({}).toArray()
238 | let activeUsers = 0
239 | let blockedUsers = 0
240 |
241 | for (let key of allUsers) {
242 | await bot.telegram.sendChatAction(key.userId, 'typing')
243 | .then((res) => {
244 | activeUsers++
245 | })
246 | .catch((err) => {
247 | blockedUsers++
248 | updateUser(ctx, false)
249 | })
250 | }
251 |
252 | ctx.reply(
253 | `⭕️ Total users: ${allUsers.length} ` +
254 | `\n✅ Active users: ${activeUsers} - ${Math.round((activeUsers / allUsers.length) * 100)}%` +
255 | `\n❌ Blocked users: ${blockedUsers} - ${Math.round((blockedUsers / allUsers.length) * 100)}%`
256 | )
257 | })
258 |
259 | bot.on('message', async (ctx) => {
260 | ctx.scene.leave('scanQR')
261 | ctx.scene.leave('generator')
262 | starter(ctx)
263 | })
264 |
265 |
266 | function starter (ctx) {
267 | ctx.reply(
268 | 'Hi! What do you want to do?',
269 | { reply_markup: { keyboard: [['🔍 Scan QR Code'], ['🖊 Generate QR Code'], ['🔍 Scan Barcode'], ['📈 Statistic', '📁 Source code']], resize_keyboard: true } }
270 | )
271 |
272 | updateUser(ctx, true)
273 | }
274 |
275 | function updateUser (ctx, active) {
276 | let jetzt = active ? 'active' : 'blocked'
277 | db.collection('allUsers').updateOne({userId: ctx.from.id}, {$set: {status: jetzt}}, {upsert: true, new: true})
278 | }
279 |
280 | function updateStat (action) {
281 | if (action == 'button') {
282 | return db.collection('statistic').updateOne({genAct: action}, {$inc: {count: 1}}, {new: true, upsert: true})
283 | }
284 |
285 | db.collection('statistic').updateOne({action: action}, {$inc: {[makeDate()]: 1}}, {new: true, upsert: true})
286 | db.collection('statistic').updateOne({genAct: action}, {$inc: {count: 1}}, {new: true, upsert: true})
287 | }
288 |
289 | function makeDate () {
290 | const today = new Date()
291 | const yyyy = today.getFullYear()
292 | let mm = today.getMonth() + 1
293 | let dd = today.getDate()
294 |
295 | dd < 10 ? dd = '0' + dd : false
296 | mm < 10 ? mm = '0' + mm : false
297 | return `${mm}/${dd}/${yyyy}`
298 | }
299 |
300 | function sendError (err, ctx) {
301 | if (err.toString().includes('message is not modified')) {
302 | return
303 | }
304 | bot.telegram.sendMessage(data.dev, `Ошибка у [${ctx.from.first_name}](tg://user?id=${ctx.from.id}) \n\nОшибка: ${err}`, { parse_mode: 'markdown' })
305 | }
--------------------------------------------------------------------------------