Added printing module
This commit is contained in:
parent
7fcd4fd5bf
commit
5876d8bc5b
1
Pipfile
1
Pipfile
|
@ -18,6 +18,7 @@ httpx = "*"
|
|||
PyYAML = "==5.3"
|
||||
wolframalpha = "*"
|
||||
Mastodon-py = "*"
|
||||
pycups = "*"
|
||||
|
||||
[dev-packages]
|
||||
pylint = "*"
|
||||
|
|
23
README.md
23
README.md
|
@ -379,9 +379,6 @@ NOTE: disabled by default
|
|||
If enabled, Jitsi calls created with Matrix clients will be sent as text messages
|
||||
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
|
||||
|
||||
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.
|
||||
|
||||
### 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
|
||||
|
||||
* Create a Matrix user
|
||||
|
|
|
@ -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"]
|
Loading…
Reference in New Issue