-
Notifications
You must be signed in to change notification settings - Fork 8
/
bin.js
executable file
·156 lines (127 loc) · 4.79 KB
/
bin.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#!/usr/bin/env node
'use strict'
const pino = require('pino')
const log = pino({name: 'tg-sticker-convert-bot'})
const emoji = require('emoji-dictionary')
const URI = require('urijs')
const path = require('path')
const fs = require('fs')
const imageminPngquant = require('imagemin-pngquant')
const imageminOptipng = require('imagemin-optipng')
const imageOptimizers = [
imageminPngquant({
quality: [0.6, 0.8],
strip: true,
speed: 1
}),
imageminOptipng({ })
]
const HELLO = `*This bot turns files into the required format for Telegram Stickers!*
Just send me your files and I'll convert them!
\\* Transparent images must be sent as files/documents, otherwise they lose transparencey
\\* Links get downloaded and converted
\\* Stickers are accepted as well
Oh, and could you please...
\\* Report bugs when you spot them: https://github.com/mkg20001/tg-sticker-convert-bot/issues
\\* Donate: https://paypal.me/mkg20001
`
const core = require('teleutils')('sticker-convert-bot', {
token: process.argv[2],
helloMessage: HELLO
})
async function doConvert (input, reply, opt) {
let output = core.tmp('_sticker_converted.png')
log.info({input: input.path, output: output.path}, 'Converting...')
await core.exec('convert', [input.path, '-alpha', 'set', '-resize', '512x512', output.path])
if (fs.lstatSync(output.path).size >= 256 * 1024) {
const buffer = fs.readFileSync(output.path)
const optimized = await Promise.all(imageOptimizers.map(optimize => optimize(buffer)))
const bestOptimized = optimized.sort((a, b) => a.length - b.length)[0]
fs.writeFileSync(output.path, bestOptimized)
}
await reply.file(output.path, opt)
// clean disk
input.cleanup()
output.cleanup()
}
const nameToPng = (name) => {
name = path.basename(name)
const parsed = path.parse(name)
parsed.ext = '.png'
delete parsed.base
return path.format(parsed)
}
const handleSticker = async (msg) => {
const location = await core.fetch.tg(msg.sticker)
if (msg.sticker.is_animated) {
await msg.track('convert/animated_sticker')
let {chat: {id: cid}, message_id: msgId} = await msg.reply.file(location.path, {fileName: (msg.sticker.emoji ? emoji.getName(msg.sticker.emoji) + '_animated_sticker' : 'animated_sticker') + '.TGS', asReply: true})
await bot.sendMessage(cid, 'You can forward this to @stickers after issuing /newanimated to create a new animated pack with this sticker', {webPreview: false, replyToMessage: msgId})
location.cleanup()
} else {
await msg.track('convert/sticker')
await doConvert(location, msg.reply, {fileName: (msg.sticker.emoji ? emoji.getName(msg.sticker.emoji) + '_sticker' : 'sticker') + '.png', asReply: true}) // can't send .webp since this gets interpreted as sticker automatically
}
}
const handleDocument = async (msg) => {
const doc = msg.document
if (!doc.mime_type.startsWith('image/')) {
return msg.reply.text('That doesn\'t look like an image')
}
const location = await core.fetch.tg(doc)
await msg.track('convert/document')
await doConvert(location, msg.reply, {fileName: nameToPng(doc.file_name), asReply: true})
}
const handlePhoto = async (msg) => {
const bestPhoto = ( // get first photo that has 512px or bigger in one dimension, otherwise get biggest TODO: check if always size sorted
msg.photo.filter(ph => ph.width >= 512 || ph.height >= 512)[0] ||
msg.photo.pop())
const location = await core.fetch.tg(bestPhoto)
await msg.track('convert/photo')
await doConvert(location, msg.reply, {fileName: 'sticker.png', asReply: true})
}
const handleText = async (msg) => {
if (msg.text.trim().startsWith('/')) { // ignore cmds
return
}
let urls = []
URI.withinString(msg.text, (url) => urls.push(url))
if (!urls.length) {
// TODO: friendly error
return msg.reply.text('Didn\'t find any URLs in your message', {asReply: true})
}
if (urls.length > 20) {
// TODO: friendly error
return msg.reply.text('Too many URLs!')
}
await Promise.all(urls.map(async (url) => {
try {
const loc = await core.fetch.web(url)
await doConvert(loc, msg.reply, {fileName: nameToPng(url), asReply: true})
} catch (e) {
// TODO: rewrite
msg.reply.text('ERROR: Couldn\'t convert ' + url, {webPreview: false, asReply: true})
log.error(e)
core.error.captureException(e)
}
}))
}
const {bot} = core
bot.on('sticker', handleSticker)
bot.on('document', handleDocument)
bot.on('photo', handlePhoto)
bot.on('text', handleText)
bot.on('forward', async (msg) => {
switch (true) {
case Boolean(msg.sticker):
return handleSticker(msg)
case Boolean(msg.document):
return handleDocument(msg)
case Boolean(msg.text):
return handleText(msg)
case Boolean(msg.photo):
return handlePhoto(msg)
default: {} // eslint-disable-line no-empty
}
})
core.start()