Initial commit
This commit is contained in:
commit
bfceff8f10
|
@ -0,0 +1,72 @@
|
||||||
|
# Telegram Printer Bot
|
||||||
|
|
||||||
|
This Python script implements a Telegram bot that can print images and stickers sent by users. The bot supports resizing images, converting them to grayscale, and applying gamma correction before printing to ensure maximum quality.
|
||||||
|
|
||||||
|
Currently, you can set a command to print your sticker (by default we used brother_ql to print to a Brother printer). You can use any external program you want to print to other brands and models of printers.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
* Python 3.6+
|
||||||
|
* telethon
|
||||||
|
* PIL (Python Imaging Library) library (Pillow)
|
||||||
|
* brother_ql
|
||||||
|
|
||||||
|
You can install the requirements by running this command:
|
||||||
|
|
||||||
|
`python3 -m pip install -r requirements.txt`
|
||||||
|
|
||||||
|
If this is the first time running the script and your printer uses the `lp` protocol, remember to add your user to the `lp` group using the following command:
|
||||||
|
|
||||||
|
`sudo usermod -a -G lp ${USER}`
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Before running the script, you need to set up the configuration parameters. You can use "config.example.py" as a guide (rename it to config.py). The following parameters must be defined:
|
||||||
|
|
||||||
|
* API_ID: Your Telegram API ID. You can obtain it by creating a Telegram application
|
||||||
|
* API_HASH: Your Telegram API hash. You can obtain it from the same page where you got the API ID.
|
||||||
|
* BOT_TOKEN: The token for your Telegram bot. You can create a new bot and obtain the token by following the instructions here.
|
||||||
|
* ADMIN_ID: Your user id. This is the user that will receive administrative rights and error reports.
|
||||||
|
* PRINT_COMMAND: Adjust with your printer model and path.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
After setting up the configuration file, you can just run the bot by using the command
|
||||||
|
|
||||||
|
`python bot.py`
|
||||||
|
|
||||||
|
Once the bot is running, it will respond to specific commands:
|
||||||
|
|
||||||
|
* `/id`: Returns your Telegram user ID, which you need to add to the ADMIN_ID list in the config.py file to grant yourself privileges.
|
||||||
|
* `/start`: Displays a welcome message and requests a password if set in the config.py file.
|
||||||
|
When the user sends the correct password in a private message, the printer functionality will be unlocked for that user.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
* Printer password (pin code) protection
|
||||||
|
* Cooldown period for users
|
||||||
|
* Caching of images and stickers
|
||||||
|
* Resizing images to the correct printer resolution for maximum crispness
|
||||||
|
* Conversion to greyscale with gamma adjustment (improves images a lot!)
|
||||||
|
* Ratio limit to prevent excessively long stickers from being printed
|
||||||
|
|
||||||
|
## Important Notes
|
||||||
|
|
||||||
|
1. Make sure to set proper permissions for the cache directory to ensure the bot can write to it.
|
||||||
|
2. The PRINT_COMMAND and PRINT_SUCCESS_COMMAND in the config.py file should be customized to match the print command on your system.
|
||||||
|
3. Ensure you have a functioning printer setup before running the bot. You will find the output from the command in the console!
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This project is licensed under the BEER-WARE License.
|
||||||
|
|
||||||
|
```/*
|
||||||
|
* ----------------------------------------------------------------------------
|
||||||
|
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||||
|
* <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you
|
||||||
|
* can do whatever you want with this stuff. If we meet some day, and you think
|
||||||
|
* this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
|
||||||
|
* ----------------------------------------------------------------------------
|
||||||
|
*/```
|
||||||
|
|
||||||
|
**This script is provided as-is, without any warranty or support. Use it at your own risk. The authors are not responsible for any misuse or damage caused by this script.**
|
|
@ -0,0 +1,111 @@
|
||||||
|
from telethon import TelegramClient
|
||||||
|
import logging, asyncio
|
||||||
|
from telethon.tl.types import MessageMediaDocument, DocumentAttributeSticker, DocumentAttributeAnimated, MessageMediaPhoto
|
||||||
|
from telethon import events
|
||||||
|
from telethon.tl.custom import Button
|
||||||
|
from os.path import isfile, join
|
||||||
|
from os import system
|
||||||
|
from time import time
|
||||||
|
from PIL import Image
|
||||||
|
from config import *
|
||||||
|
from os import makedirs
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
|
||||||
|
client = TelegramClient('bot', API_ID, API_HASH).start(bot_token=BOT_TOKEN)
|
||||||
|
client.flood_sleep_threshold = 120
|
||||||
|
|
||||||
|
print_log = {}
|
||||||
|
|
||||||
|
@client.on(events.NewMessage(pattern='^/id'))
|
||||||
|
async def debug_id(ev):
|
||||||
|
await ev.respond(f"Hello! Your id is `{ev.peer_id.user_id}` please add it to the ADMIN_ID to give youself privileges :)")
|
||||||
|
|
||||||
|
@client.on(events.NewMessage(pattern='^/start'))
|
||||||
|
async def welcome(ev):
|
||||||
|
await ev.respond(WELCOME_MSG)
|
||||||
|
if (ev.peer_id.user_id not in print_log) and PASSWORD:
|
||||||
|
await ev.respond(UNLOCK_MSG)
|
||||||
|
|
||||||
|
# This one triggers on a single message with the pin code written
|
||||||
|
@client.on(events.NewMessage(pattern=PASSWORD, func=lambda e: e.is_private))
|
||||||
|
async def unlock_printer(ev):
|
||||||
|
if ev.peer_id.user_id not in print_log:
|
||||||
|
print_log[ev.peer_id.user_id] = 0
|
||||||
|
if PASSWORD:
|
||||||
|
await ev.respond(UNLOCKED_MSG)
|
||||||
|
|
||||||
|
@client.on(events.NewMessage(incoming=True, func=lambda e: e.is_private and e.message.media))
|
||||||
|
async def handler(ev):
|
||||||
|
|
||||||
|
msg = ev.message
|
||||||
|
if ev.peer_id.user_id not in print_log:
|
||||||
|
await ev.respond(UNLOCK_MSG)
|
||||||
|
|
||||||
|
# Check if the file is valid
|
||||||
|
if msg.photo:
|
||||||
|
fn = join(CACHE_DIR, f"{msg.photo.id}.jpg")
|
||||||
|
elif msg.sticker:
|
||||||
|
fn = join(CACHE_DIR, f"{msg.sticker.id}.webp")
|
||||||
|
for att in msg.sticker.attributes:
|
||||||
|
if isinstance(att, DocumentAttributeAnimated):
|
||||||
|
fn = None
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
fn = None
|
||||||
|
|
||||||
|
if not fn:
|
||||||
|
await ev.respond(FORMAT_ERR_MSG)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Check if the user is still in the cooldown period
|
||||||
|
time_left = int((print_log[ev.peer_id.user_id] + BASE_COOLDOWN) - time())
|
||||||
|
if time_left > 0:
|
||||||
|
await ev.respond(RATELIMIT_MSG.format(time_left=time_left))
|
||||||
|
return
|
||||||
|
|
||||||
|
# Download the file unless it's in the cache!
|
||||||
|
if not isfile(fn):
|
||||||
|
await client.download_media(msg, file=fn)
|
||||||
|
|
||||||
|
# Try opening the image, at least
|
||||||
|
try:
|
||||||
|
img = Image.open(fn)
|
||||||
|
except:
|
||||||
|
await ev.respond(FORMAT_ERR_MSG)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Limit stickers ratio (so people don't print incredibly long stickers)
|
||||||
|
if img.size[1]/img.size[0] > MAX_ASPECT_RATIO:
|
||||||
|
return ev.respond(RATIO_ERR_MSG)
|
||||||
|
|
||||||
|
# Remove transparency
|
||||||
|
if img.mode == 'RGBA':
|
||||||
|
bg_img = Image.new(img.mode, img.size, BACKGROUND_COLOR)
|
||||||
|
img = Image.alpha_composite(bg_img, img)
|
||||||
|
|
||||||
|
# Resize the image
|
||||||
|
img.thumbnail([WIDTH_PX, HEIGHT_PX], resample=Image.LANCZOS, reducing_gap=None)
|
||||||
|
|
||||||
|
# Convert to grayscale and apply a gamma of 1.8
|
||||||
|
img = img.convert('L')
|
||||||
|
|
||||||
|
if GAMMA_CORRECTION != 1:
|
||||||
|
img = Image.eval(img, lambda x: int(255*pow((x/255),(1/GAMMA_CORRECTION))))
|
||||||
|
|
||||||
|
img.save(IMAGE_PATH, 'PNG')
|
||||||
|
|
||||||
|
status_code = system(PRINT_COMMAND)
|
||||||
|
if status_code == 0:
|
||||||
|
print_log[ev.peer_id.user_id] = time()
|
||||||
|
await ev.respond(PRINT_SUCCESS_MSG)
|
||||||
|
else:
|
||||||
|
await ev.respond(PRINT_FAIL_MSG)
|
||||||
|
await client.send_message(ADMIN_ID, f'Printer is not working. Process returned status code {status_code}')
|
||||||
|
|
||||||
|
if PRINT_SUCCESS_COMMAND:
|
||||||
|
system(PRINT_SUCCESS_COMMAND)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
makedirs(CACHE_DIR, exist_ok=True)
|
||||||
|
client.run_until_disconnected()
|
|
@ -0,0 +1,37 @@
|
||||||
|
|
||||||
|
# To get an API_ID and API_HASH, you need to sign up to be a Telegram Dev.
|
||||||
|
# Do it here: https://core.telegram.org/api/obtaining_api_id
|
||||||
|
API_ID = 11111
|
||||||
|
API_HASH = '5d41402abc4b2a76b9719d911017c592'
|
||||||
|
|
||||||
|
# Get a bot token from Telegram by creating a bot with @BotFather
|
||||||
|
BOT_TOKEN = '1222222222:b2YgZXdvaWZld29maHdlb2lmaA'
|
||||||
|
|
||||||
|
WELCOME_MSG = 'Hello!\nWelcome to **Foxo\'s label printer**! Send me any sticker or other media to print it!'
|
||||||
|
UNLOCK_MSG = 'The printer is currently locked for you. Please enter the password!'
|
||||||
|
PRINT_FAIL_MSG = 'I wasn\'t able to print your sticker.'
|
||||||
|
RATIO_ERR_MSG = 'That image is too tall. It would waste a lot of paper. Please give me a shorter sticker.'
|
||||||
|
PRINT_SUCCESS_MSG = 'Your sticker has finished printing now! Enjoy it :3'
|
||||||
|
FORMAT_ERR_MSG = 'Cannot print this. Try with a (static) sticker or a picture!'
|
||||||
|
RATELIMIT_MSG = 'Woo calm down fam!\n\nSend the sticker again in {time_left} seconds!'
|
||||||
|
UNLOCKED_MSG = 'Printer has been unlocked. Have fun!'
|
||||||
|
|
||||||
|
# Limits to prevent abuse
|
||||||
|
PASSWORD = '12345' # Set to None if no password required
|
||||||
|
BASE_COOLDOWN = 10 # Seconds between stickers printing
|
||||||
|
MAX_ASPECT_RATIO = 1.5 # Maximum ratio between height/width of sticker
|
||||||
|
ADMIN_ID = 111111 # Find your own id with the /id command
|
||||||
|
|
||||||
|
# Folder settings
|
||||||
|
IMAGE_PATH = '/tmp/image.png'
|
||||||
|
CACHE_DIR = '/tmp/printercache'
|
||||||
|
|
||||||
|
# Remember to add your user to the "lp" group or this won't work!
|
||||||
|
PRINT_COMMAND = f"brother_ql -m QL-700 -b linux_kernel -p file:///dev/usb/lp0 print -l 62 {IMAGE_PATH} -d"
|
||||||
|
PRINT_SUCCESS_COMMAND = None # "mpv --no-video success.wav" - this was used to play audio
|
||||||
|
|
||||||
|
# Resize and process settings
|
||||||
|
WIDTH_PX = 696
|
||||||
|
HEIGHT_PX = 9999 # This means "do not care about height"
|
||||||
|
GAMMA_CORRECTION = 1.8
|
||||||
|
BACKGROUND_COLOR = 'white'
|
|
@ -0,0 +1,3 @@
|
||||||
|
telethon
|
||||||
|
Pillow
|
||||||
|
brother_ql
|
Loading…
Reference in New Issue