Added printing module

This commit is contained in:
Ville Ranki 2020-11-23 00:20:09 +02:00
parent 7fcd4fd5bf
commit 5876d8bc5b
3 changed files with 131 additions and 3 deletions

View File

@ -18,6 +18,7 @@ httpx = "*"
PyYAML = "==5.3" PyYAML = "==5.3"
wolframalpha = "*" wolframalpha = "*"
Mastodon-py = "*" Mastodon-py = "*"
pycups = "*"
[dev-packages] [dev-packages]
pylint = "*" pylint = "*"

View File

@ -379,9 +379,6 @@ NOTE: disabled by default
If enabled, Jitsi calls created with Matrix clients will be sent as text messages If enabled, Jitsi calls created with Matrix clients will be sent as text messages
to rooms, allowing non-matrix users to join them. to rooms, allowing non-matrix users to join them.
Note: Currently supports only calls placed from Element Web. Android version
sends different messages and has not yet been reverse engineered.
### Mastodon ### Mastodon
Send toots to Mastodon. You can login to Mastodon with the bot and toot with it. Send toots to Mastodon. You can login to Mastodon with the bot and toot with it.
@ -432,6 +429,26 @@ File uploads, joins, leaves or other special events are not (yet) handled. Contr
Relaybots are stupid. Please prefer real Matrix bridges to this. Sometimes there's no alternative. Relaybots are stupid. Please prefer real Matrix bridges to this. Sometimes there's no alternative.
### Printing
With this module you can set up a room to print any uploaded files on a specified printer.
The printer must be visible to bot via CUPS.
Commands (all can be done by bot owner only):
* !printing list - Lists available printers and their rooms
* !printing setroomprinter [printername] - Assignes given printer to this room
* !printing rmroomprinter - Deletes printer from this room
The module sends the files to CUPS for printing so please see CUPS documentation
on what works and what doesn't.
Tested formats: PDF, JPG, PNG
SVG files are printed as text currently, avoid printing them.
This module is disabled by default.
## Bot setup ## Bot setup
* Create a Matrix user * Create a Matrix user

110
modules/printing.py Normal file
View File

@ -0,0 +1,110 @@
from modules.common.module import BotModule
from nio import RoomMessageMedia
from typing import Optional
import sys
import traceback
import cups
import httpx
import asyncio
import aiofiles
import os
# Credit: https://medium.com/swlh/how-to-boost-your-python-apps-using-httpx-and-asynchronous-calls-9cfe6f63d6ad
async def download_file(url: str, filename: Optional[str] = None) -> str:
filename = filename or url.split("/")[-1]
filename = f"/tmp/{filename}"
client = httpx.AsyncClient()
async with client.stream("GET", url) as resp:
resp.raise_for_status()
async with aiofiles.open(filename, "wb") as f:
async for data in resp.aiter_bytes():
if data:
await f.write(data)
await client.aclose()
return filename
class MatrixModule(BotModule):
def __init__(self, name):
super().__init__(name)
self.printers = dict() # roomid <-> printername
self.bot = None
self.paper_size = 'A4' # Todo: configurable
self.enabled = False
async def file_cb(self, room, event):
try:
if self.bot.should_ignore_event(event):
return
if room.room_id in self.printers:
printer = self.printers[room.room_id]
self.logger.debug(f'RX file - MXC {event.url} - from {event.sender}')
https_url = await self.bot.client.mxc_to_http(event.url)
self.logger.debug(f'HTTPS URL {https_url}')
filename = await download_file(https_url)
self.logger.debug(f'RX filename {filename}')
conn = cups.Connection ()
conn.printFile(printer, filename, f"Printed from Matrix - {filename}", {'fit-to-page': 'TRUE', 'PageSize': self.paper_size})
await self.bot.send_text(room, f'Printing file on {printer}..')
os.remove(filename) # Not sure if we should wait first?
else:
self.logger.debug(f'No printer configured for room {room.room_id}')
except:
self.logger.warning(f"File callback failure")
traceback.print_exc(file=sys.stderr)
await self.bot.send_text(room, f'Printing failed, sorry. See log for details.')
def matrix_start(self, bot):
super().matrix_start(bot)
bot.client.add_event_callback(self.file_cb, RoomMessageMedia)
self.bot = bot
def matrix_stop(self, bot):
super().matrix_stop(bot)
bot.remove_callback(self.file_cb)
self.bot = None
async def matrix_message(self, bot, room, event):
bot.must_be_owner(event)
args = event.body.split()
args.pop(0)
conn = cups.Connection ()
printers = conn.getPrinters ()
if len(args) == 1:
if args[0] == 'list':
msg = f"Available printers:\n"
for printer in printers:
print(printer, printers[printer]["device-uri"])
msg += f' - {printer} / {printers[printer]["device-uri"]}'
for roomid, printerid in self.printers.items():
if printerid == printer:
msg += f' <- room {roomid}'
msg += '\n'
await bot.send_text(room, msg)
elif args[0] == 'rmroomprinter':
del self.printers[room.room_id]
await bot.send_text(room, f'Deleted printer from this room.')
bot.save_settings()
if len(args) == 2:
if args[0] == 'setroomprinter':
printer = args[1]
if printer in printers:
await bot.send_text(room, f'Printing with {printer} here.')
self.printers[room.room_id] = printer
bot.save_settings()
else:
await bot.send_text(room, f'No printer called {printer} in your CUPS.')
def help(self):
return 'Print files from Matrix'
def get_settings(self):
data = super().get_settings()
data["printers"] = self.printers
return data
def set_settings(self, data):
super().set_settings(data)
if data.get("printers"):
self.printers = data["printers"]