this fixed it
This commit is contained in:
parent
057fa1dca8
commit
cd6a86dfb9
16
bot.py
16
bot.py
|
@ -24,19 +24,7 @@ from PIL import Image
|
||||||
import requests
|
import requests
|
||||||
from nio import AsyncClient, InviteEvent, JoinError, RoomMessageText, MatrixRoom, LoginError, RoomMemberEvent, RoomVisibility, RoomPreset, RoomCreateError, RoomResolveAliasResponse, UploadError, UploadResponse, SyncError
|
from nio import AsyncClient, InviteEvent, JoinError, RoomMessageText, MatrixRoom, LoginError, RoomMemberEvent, RoomVisibility, RoomPreset, RoomCreateError, RoomResolveAliasResponse, UploadError, UploadResponse, SyncError
|
||||||
|
|
||||||
# Couple of custom exceptions
|
from modules.common.exceptions import CommandRequiresAdmin, CommandRequiresOwner, UploadFailed
|
||||||
|
|
||||||
|
|
||||||
class UploadFailed(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class CommandRequiresAdmin(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class CommandRequiresOwner(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class Bot:
|
class Bot:
|
||||||
|
|
||||||
|
@ -115,7 +103,7 @@ class Bot:
|
||||||
except ValueError: # broken cache?
|
except ValueError: # broken cache?
|
||||||
self.logger.warning(f"Image cache for {url} could not be unpacked, attempting to re-upload...")
|
self.logger.warning(f"Image cache for {url} could not be unpacked, attempting to re-upload...")
|
||||||
try:
|
try:
|
||||||
matrix_uri, mimetype, w, h, size = await self.upload_image(url, event=event, blob=blob, no_cache=no_cache)
|
matrix_uri, mimetype, w, h, size = await self.upload_image(url, blob=blob, no_cache=no_cache)
|
||||||
except (UploadFailed, ValueError):
|
except (UploadFailed, ValueError):
|
||||||
return await self.send_text(room, f"Sorry. Something went wrong fetching {url} and uploading the image to matrix server :(", event=event)
|
return await self.send_text(room, f"Sorry. Something went wrong fetching {url} and uploading the image to matrix server :(", event=event)
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ from nio import AsyncClient, UploadError
|
||||||
from nio import UploadResponse
|
from nio import UploadResponse
|
||||||
|
|
||||||
from modules.common.module import BotModule
|
from modules.common.module import BotModule
|
||||||
from bot import UploadFailed
|
from modules.common.exceptions import UploadFailed
|
||||||
|
|
||||||
|
|
||||||
class Apod:
|
class Apod:
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
# Couple of custom exceptions
|
||||||
|
|
||||||
|
|
||||||
|
class UploadFailed(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class CommandRequiresAdmin(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class CommandRequiresOwner(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,6 @@ import sys
|
||||||
import traceback
|
import traceback
|
||||||
import cups
|
import cups
|
||||||
import httpx
|
import httpx
|
||||||
import asyncio
|
|
||||||
import aiofiles
|
import aiofiles
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
|
@ -1,81 +1,98 @@
|
||||||
|
import time
|
||||||
import urllib.request
|
import urllib.request
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
import urllib.error
|
import urllib.error
|
||||||
import datetime
|
|
||||||
import pytz
|
import aiohttp.web
|
||||||
|
import requests
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
import importlib
|
|
||||||
from importlib import reload
|
|
||||||
|
|
||||||
from nio import MatrixRoom
|
|
||||||
|
|
||||||
from aiohttp import web
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import nest_asyncio
|
from aiohttp import web
|
||||||
nest_asyncio.apply()
|
from future.moves.urllib.parse import urlencode
|
||||||
|
from nio import MatrixRoom
|
||||||
|
|
||||||
from modules.common.module import BotModule
|
from modules.common.module import BotModule
|
||||||
|
|
||||||
tautulli_path = os.getenv("TAUTULLI_PATH")
|
import nest_asyncio
|
||||||
|
nest_asyncio.apply()
|
||||||
|
|
||||||
def load_tzlocal():
|
rooms = dict()
|
||||||
try:
|
global_bot = None
|
||||||
global tautulli_path
|
|
||||||
|
|
||||||
sys.path.insert(0, tautulli_path)
|
|
||||||
sys.path.insert(0, "{}/lib".format(tautulli_path))
|
async def send_entry(blob, content_type, fmt_params, rooms):
|
||||||
module = importlib.import_module("tzlocal")
|
for room_id in rooms:
|
||||||
module = reload(module)
|
room = MatrixRoom(room_id=room_id, own_user_id=os.getenv("BOT_OWNERS"),
|
||||||
return module
|
encrypted=rooms[room_id])
|
||||||
except ModuleNotFoundError:
|
if blob and content_type:
|
||||||
|
await global_bot.upload_and_send_image(room, blob, text="", blob=True, blob_content_type=content_type)
|
||||||
|
|
||||||
|
await global_bot.send_html(room, msg_template_html.format(**fmt_params),
|
||||||
|
msg_template_plain.format(**fmt_params))
|
||||||
|
|
||||||
|
|
||||||
|
def get_image(img=None, width=1000, height=1500):
|
||||||
|
"""
|
||||||
|
Return image data as array.
|
||||||
|
Array contains the image content type and image binary
|
||||||
|
|
||||||
|
Parameters required: img { Plex image location }
|
||||||
|
Optional parameters: width { the image width }
|
||||||
|
height { the image height }
|
||||||
|
Output: array
|
||||||
|
"""
|
||||||
|
|
||||||
|
pms_url = os.getenv("PLEX_MEDIA_SERVER_URL")
|
||||||
|
pms_token = os.getenv("PLEX_MEDIA_SERVER_TOKEN")
|
||||||
|
if not pms_url or not pms_token:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def load_tautulli():
|
width = width or 1000
|
||||||
try:
|
height = height or 1500
|
||||||
global tautulli_path
|
|
||||||
|
|
||||||
sys.path.insert(0, tautulli_path)
|
if img:
|
||||||
sys.path.insert(0, "{}/lib".format(tautulli_path))
|
params = {'url': 'http://127.0.0.1:32400%s' % (img), 'width': width, 'height': height, 'format': "png"}
|
||||||
module = importlib.import_module("plexpy")
|
|
||||||
module = reload(module)
|
|
||||||
return module
|
|
||||||
except ModuleNotFoundError:
|
|
||||||
return None
|
|
||||||
|
|
||||||
plexpy = load_tautulli()
|
uri = pms_url + '/photo/:/transcode?%s' % urlencode(params)
|
||||||
tzlocal = load_tzlocal()
|
|
||||||
|
|
||||||
send_entry_lock = asyncio.Lock()
|
headers = {'X-Plex-Token': pms_token}
|
||||||
|
|
||||||
async def send_entry(bot, room, entry):
|
session = requests.Session()
|
||||||
global send_entry_lock
|
try:
|
||||||
async with send_entry_lock:
|
r = session.request("GET", uri, headers=headers)
|
||||||
if "art" in entry:
|
r.raise_for_status()
|
||||||
global plexpy
|
except Exception:
|
||||||
if plexpy:
|
return None
|
||||||
pms = plexpy.pmsconnect.PmsConnect()
|
|
||||||
pms_image = pms.get_image(entry["art"], 600, 300)
|
|
||||||
if pms_image:
|
|
||||||
(blob, content_type) = pms_image
|
|
||||||
await bot.upload_and_send_image(room, blob, "", True, content_type)
|
|
||||||
|
|
||||||
fmt_params = {
|
response_status = r.status_code
|
||||||
"title": entry["title"],
|
response_content = r.content
|
||||||
"year": entry["year"],
|
response_headers = r.headers
|
||||||
"audience_rating": entry["audience_rating"],
|
if response_status in (200, 201):
|
||||||
"directors": ", ".join(entry["directors"]),
|
return response_content, response_headers['Content-Type']
|
||||||
"actors": ", ".join(entry["actors"]),
|
|
||||||
"summary": entry["summary"],
|
|
||||||
"tagline": entry["tagline"],
|
def get_from_entry(entry):
|
||||||
"genres": ", ".join(entry["genres"])
|
blob = None
|
||||||
}
|
content_type = ""
|
||||||
|
if "art" in entry:
|
||||||
|
pms_image = get_image(entry["art"], 600, 300)
|
||||||
|
if pms_image:
|
||||||
|
(blob, content_type) = pms_image
|
||||||
|
|
||||||
|
fmt_params = {
|
||||||
|
"title": entry["title"],
|
||||||
|
"year": entry["year"],
|
||||||
|
"audience_rating": entry["audience_rating"],
|
||||||
|
"directors": ", ".join(entry["directors"]),
|
||||||
|
"actors": ", ".join(entry["actors"]),
|
||||||
|
"summary": entry["summary"],
|
||||||
|
"tagline": entry["tagline"],
|
||||||
|
"genres": ", ".join(entry["genres"])
|
||||||
|
}
|
||||||
|
|
||||||
|
return (blob, content_type, fmt_params)
|
||||||
|
|
||||||
await bot.send_html(room,
|
|
||||||
msg_template_html.format(**fmt_params),
|
|
||||||
msg_template_plain.format(**fmt_params))
|
|
||||||
|
|
||||||
msg_template_html = """
|
msg_template_html = """
|
||||||
<b>{title} -({year})- Rating: {audience_rating}</b><br>
|
<b>{title} -({year})- Rating: {audience_rating}</b><br>
|
||||||
|
@ -94,25 +111,29 @@ msg_template_plain = """*{title} -({year})- Rating: {audience_rating}*
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class WebServer:
|
|
||||||
bot = None
|
|
||||||
rooms = dict()
|
|
||||||
|
|
||||||
|
def _get_loop():
|
||||||
|
try:
|
||||||
|
loop = asyncio.new_event_loop()
|
||||||
|
asyncio.set_event_loop(loop)
|
||||||
|
except RuntimeError:
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
finally:
|
||||||
|
return loop
|
||||||
|
|
||||||
|
|
||||||
|
class WebServer:
|
||||||
def __init__(self, host, port):
|
def __init__(self, host, port):
|
||||||
self.app = web.Application()
|
|
||||||
self.host = host
|
self.host = host
|
||||||
self.port = port
|
self.port = port
|
||||||
|
self.app = web.Application()
|
||||||
self.app.router.add_post('/notify', self.notify)
|
self.app.router.add_post('/notify', self.notify)
|
||||||
|
|
||||||
async def run(self):
|
def run(self):
|
||||||
if not self.host or not self.port:
|
if not self.host or not self.port:
|
||||||
return
|
return
|
||||||
|
|
||||||
loop = asyncio.get_event_loop()
|
aiohttp.web.run_app(self.app, host=self.host, port=self.port, handle_signals=True, loop=asyncio.get_running_loop())
|
||||||
runner = web.AppRunner(self.app)
|
|
||||||
loop.run_until_complete(runner.setup())
|
|
||||||
site = web.TCPSite(runner, host=self.host, port=self.port)
|
|
||||||
loop.run_until_complete(site.start())
|
|
||||||
|
|
||||||
async def notify(self, request: web.Request) -> web.Response:
|
async def notify(self, request: web.Request) -> web.Response:
|
||||||
try:
|
try:
|
||||||
|
@ -126,9 +147,9 @@ class WebServer:
|
||||||
if "directors" in data:
|
if "directors" in data:
|
||||||
data["directors"] = data["directors"].split(",")
|
data["directors"] = data["directors"].split(",")
|
||||||
|
|
||||||
for room_id in self.rooms:
|
global rooms
|
||||||
room = MatrixRoom(room_id=room_id, own_user_id=os.getenv("BOT_OWNERS"), encrypted=self.rooms[room_id])
|
(blob, content_type, fmt_params) = get_from_entry(data)
|
||||||
await send_entry(self.bot, room, data)
|
await send_entry(blob, content_type, fmt_params, rooms)
|
||||||
|
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
message = str(exc)
|
message = str(exc)
|
||||||
|
@ -136,6 +157,7 @@ class WebServer:
|
||||||
|
|
||||||
return web.Response()
|
return web.Response()
|
||||||
|
|
||||||
|
|
||||||
class MatrixModule(BotModule):
|
class MatrixModule(BotModule):
|
||||||
httpd = None
|
httpd = None
|
||||||
rooms = dict()
|
rooms = dict()
|
||||||
|
@ -143,29 +165,16 @@ class MatrixModule(BotModule):
|
||||||
|
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
super().__init__(name)
|
super().__init__(name)
|
||||||
global plexpy
|
|
||||||
if plexpy:
|
|
||||||
global tautulli_path
|
|
||||||
plexpy.FULL_PATH = "{}/Tautulli.py".format(tautulli_path)
|
|
||||||
plexpy.PROG_DIR = os.path.dirname(plexpy.FULL_PATH)
|
|
||||||
plexpy.DATA_DIR = tautulli_path
|
|
||||||
plexpy.DB_FILE = os.path.join(plexpy.DATA_DIR, plexpy.database.FILENAME)
|
|
||||||
|
|
||||||
try:
|
|
||||||
plexpy.SYS_TIMEZONE = tzlocal.get_localzone()
|
|
||||||
except (pytz.UnknownTimeZoneError, LookupError, ValueError) as e:
|
|
||||||
plexpy.SYS_TIMEZONE = pytz.UTC
|
|
||||||
|
|
||||||
plexpy.SYS_UTC_OFFSET = datetime.datetime.now(plexpy.SYS_TIMEZONE).strftime('%z')
|
|
||||||
plexpy.initialize("{}/config.ini".format(tautulli_path))
|
|
||||||
|
|
||||||
self.httpd = WebServer(os.getenv("TAUTULLI_NOTIFIER_ADDR"), os.getenv("TAUTULLI_NOTIFIER_PORT"))
|
self.httpd = WebServer(os.getenv("TAUTULLI_NOTIFIER_ADDR"), os.getenv("TAUTULLI_NOTIFIER_PORT"))
|
||||||
loop = asyncio.get_event_loop()
|
|
||||||
loop.run_until_complete(self.httpd.run())
|
|
||||||
|
|
||||||
def matrix_start(self, bot):
|
def matrix_start(self, bot):
|
||||||
super().matrix_start(bot)
|
super().matrix_start(bot)
|
||||||
self.httpd.bot = bot
|
global global_bot
|
||||||
|
global_bot = bot
|
||||||
|
self.httpd.run()
|
||||||
|
|
||||||
|
def matrix_stop(self, bot):
|
||||||
|
super().matrix_stop(bot)
|
||||||
|
|
||||||
async def matrix_message(self, bot, room, event):
|
async def matrix_message(self, bot, room, event):
|
||||||
args = event.body.split()
|
args = event.body.split()
|
||||||
|
@ -183,7 +192,7 @@ class MatrixModule(BotModule):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
url = "{}/api/v2?apikey={}&cmd=get_recently_added&count=10".format(os.getenv("TAUTULLI_URL"), self.api_key)
|
url = "{}/api/v2?apikey={}&cmd=get_recently_added&count=10".format(os.getenv("TAUTULLI_URL"), self.api_key)
|
||||||
req = urllib.request.Request(url+"&media_type="+media_type)
|
req = urllib.request.Request(url + "&media_type=" + media_type)
|
||||||
connection = urllib.request.urlopen(req).read()
|
connection = urllib.request.urlopen(req).read()
|
||||||
entries = json.loads(connection)
|
entries = json.loads(connection)
|
||||||
if "response" not in entries and "data" not in entries["response"] and "recently_added" not in entries["response"]["data"]:
|
if "response" not in entries and "data" not in entries["response"] and "recently_added" not in entries["response"]["data"]:
|
||||||
|
@ -191,7 +200,8 @@ class MatrixModule(BotModule):
|
||||||
return
|
return
|
||||||
|
|
||||||
for entry in entries["response"]["data"]["recently_added"]:
|
for entry in entries["response"]["data"]["recently_added"]:
|
||||||
await send_entry(bot, room, entry)
|
(blob, content_type, fmt_params) = get_from_entry(entry)
|
||||||
|
await send_entry(blob, content_type, fmt_params, {room.room_id: room})
|
||||||
|
|
||||||
except urllib.error.HTTPError as err:
|
except urllib.error.HTTPError as err:
|
||||||
raise ValueError(err.read())
|
raise ValueError(err.read())
|
||||||
|
@ -210,7 +220,8 @@ class MatrixModule(BotModule):
|
||||||
await bot.send_text(room, f"Removed {room_id} to rooms notification list")
|
await bot.send_text(room, f"Removed {room_id} to rooms notification list")
|
||||||
|
|
||||||
bot.save_settings()
|
bot.save_settings()
|
||||||
self.httpd.rooms = self.rooms
|
global rooms
|
||||||
|
rooms = self.rooms
|
||||||
else:
|
else:
|
||||||
await bot.send_text(room, 'Usage: !tautulli <movie|show|artist>|<add|remove> %room_id% %encrypted%')
|
await bot.send_text(room, 'Usage: !tautulli <movie|show|artist>|<add|remove> %room_id% %encrypted%')
|
||||||
else:
|
else:
|
||||||
|
@ -220,16 +231,19 @@ class MatrixModule(BotModule):
|
||||||
data = super().get_settings()
|
data = super().get_settings()
|
||||||
data["api_key"] = self.api_key
|
data["api_key"] = self.api_key
|
||||||
data["rooms"] = self.rooms
|
data["rooms"] = self.rooms
|
||||||
self.httpd.rooms = self.rooms
|
global rooms
|
||||||
|
rooms = self.rooms
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def set_settings(self, data):
|
def set_settings(self, data):
|
||||||
super().set_settings(data)
|
super().set_settings(data)
|
||||||
if data.get("rooms"):
|
if data.get("rooms"):
|
||||||
self.rooms = data["rooms"]
|
self.rooms = data["rooms"]
|
||||||
self.httpd.rooms = self.rooms
|
global rooms
|
||||||
|
rooms = self.rooms
|
||||||
if data.get("api_key"):
|
if data.get("api_key"):
|
||||||
self.api_key = data["api_key"]
|
self.api_key = data["api_key"]
|
||||||
|
|
||||||
def help(self):
|
def help(self):
|
||||||
return ('Tautulli recently added bot')
|
return ('Tautulli recently added bot')
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue