summaryrefslogtreecommitdiff
path: root/src/home
diff options
context:
space:
mode:
Diffstat (limited to 'src/home')
-rw-r--r--src/home/api/errors/api_response_error.py4
-rw-r--r--src/home/api/types/types.py1
-rw-r--r--src/home/api/web_api_client.py20
-rw-r--r--src/home/audio/amixer.py4
-rw-r--r--src/home/bot/__init__.py2
-rw-r--r--src/home/bot/lang.py13
-rw-r--r--src/home/bot/wrapper.py34
-rw-r--r--src/home/camera/util.py3
-rw-r--r--src/home/database/bots.py10
-rw-r--r--src/home/media/node_client.py6
-rw-r--r--src/home/media/record.py6
-rw-r--r--src/home/media/record_client.py18
-rw-r--r--src/home/media/storage.py4
-rw-r--r--src/home/telegram/telegram.py3
-rw-r--r--src/home/util.py6
15 files changed, 84 insertions, 50 deletions
diff --git a/src/home/api/errors/api_response_error.py b/src/home/api/errors/api_response_error.py
index 6910b2d..85d788b 100644
--- a/src/home/api/errors/api_response_error.py
+++ b/src/home/api/errors/api_response_error.py
@@ -1,4 +1,4 @@
-from typing import Optional
+from typing import Optional, List
class ApiResponseError(Exception):
@@ -6,7 +6,7 @@ class ApiResponseError(Exception):
status_code: int,
error_type: str,
error_message: str,
- error_stacktrace: Optional[list[str]] = None):
+ error_stacktrace: Optional[List[str]] = None):
super().__init__()
self.status_code = status_code
self.error_message = error_message
diff --git a/src/home/api/types/types.py b/src/home/api/types/types.py
index b6233e6..4d8b4ff 100644
--- a/src/home/api/types/types.py
+++ b/src/home/api/types/types.py
@@ -7,6 +7,7 @@ class BotType(Enum):
SENSORS = auto()
ADMIN = auto()
SOUND = auto()
+ POLARIS_KETTLE = auto()
class TemperatureSensorLocation(Enum):
diff --git a/src/home/api/web_api_client.py b/src/home/api/web_api_client.py
index 34d080c..d6c9dc7 100644
--- a/src/home/api/web_api_client.py
+++ b/src/home/api/web_api_client.py
@@ -6,7 +6,7 @@ import logging
from collections import namedtuple
from datetime import datetime
from enum import Enum, auto
-from typing import Optional, Callable, Union
+from typing import Optional, Callable, Union, List, Tuple, Dict
from requests.auth import HTTPBasicAuth
from .errors import ApiResponseError
@@ -28,13 +28,13 @@ class HTTPMethod(Enum):
class WebAPIClient:
token: str
- timeout: Union[float, tuple[float, float]]
+ timeout: Union[float, Tuple[float, float]]
basic_auth: Optional[HTTPBasicAuth]
do_async: bool
async_error_handler: Optional[Callable]
async_success_handler: Optional[Callable]
- def __init__(self, timeout: Union[float, tuple[float, float]] = 5):
+ def __init__(self, timeout: Union[float, Tuple[float, float]] = 5):
self.token = config['api']['token']
self.timeout = timeout
self.basic_auth = None
@@ -66,7 +66,7 @@ class WebAPIClient:
})
def log_openwrt(self,
- lines: list[tuple[int, str]]):
+ lines: List[Tuple[int, str]]):
return self._post('logs/openwrt', {
'logs': stringify(lines)
})
@@ -81,14 +81,14 @@ class WebAPIClient:
return [(datetime.fromtimestamp(date), temp, hum) for date, temp, hum in data]
def add_sound_sensor_hits(self,
- hits: list[tuple[str, int]]):
+ hits: List[Tuple[str, int]]):
return self._post('sound_sensors/hits/', {
'hits': stringify(hits)
})
def get_sound_sensor_hits(self,
location: SoundSensorLocation,
- after: datetime) -> list[dict]:
+ after: datetime) -> List[dict]:
return self._process_sound_sensor_hits_data(self._get('sound_sensors/hits/', {
'after': int(after.timestamp()),
'location': location.value
@@ -100,13 +100,13 @@ class WebAPIClient:
'location': location.value
}))
- def recordings_list(self, extended=False, as_objects=False) -> Union[list[str], list[dict], list[RecordFile]]:
+ def recordings_list(self, extended=False, as_objects=False) -> Union[List[str], List[dict], List[RecordFile]]:
files = self._get('recordings/list/', {'extended': int(extended)})['data']
if as_objects:
return MediaNodeClient.record_list_from_serialized(files)
return files
- def _process_sound_sensor_hits_data(self, data: list[dict]) -> list[dict]:
+ def _process_sound_sensor_hits_data(self, data: List[dict]) -> List[dict]:
for item in data:
item['time'] = datetime.fromtimestamp(item['time'])
return data
@@ -124,7 +124,7 @@ class WebAPIClient:
name: str,
params: dict,
method: HTTPMethod,
- files: Optional[dict[str, str]] = None):
+ files: Optional[Dict[str, str]] = None):
if not self.do_async:
return self._make_request(name, params, method, files)
else:
@@ -136,7 +136,7 @@ class WebAPIClient:
name: str,
params: dict,
method: HTTPMethod = HTTPMethod.GET,
- files: Optional[dict[str, str]] = None) -> Optional[any]:
+ files: Optional[Dict[str, str]] = None) -> Optional[any]:
domain = config['api']['host']
kwargs = {}
diff --git a/src/home/audio/amixer.py b/src/home/audio/amixer.py
index 0ab2c64..53e6bce 100644
--- a/src/home/audio/amixer.py
+++ b/src/home/audio/amixer.py
@@ -2,7 +2,7 @@ import subprocess
from ..config import config
from threading import Lock
-from typing import Union
+from typing import Union, List
_lock = Lock()
@@ -16,7 +16,7 @@ def has_control(s: str) -> bool:
return False
-def get_caps(s: str) -> list[str]:
+def get_caps(s: str) -> List[str]:
for control in config['amixer']['controls']:
if control['name'] == s:
return control['caps']
diff --git a/src/home/bot/__init__.py b/src/home/bot/__init__.py
index 5e68af7..0d93af3 100644
--- a/src/home/bot/__init__.py
+++ b/src/home/bot/__init__.py
@@ -1,6 +1,6 @@
from .reporting import ReportingHelper
from .lang import LangPack
-from .wrapper import Wrapper, Context, text_filter
+from .wrapper import Wrapper, Context, text_filter, handlermethod
from .store import Store
from .errors import *
from .util import command_usage, user_any_name \ No newline at end of file
diff --git a/src/home/bot/lang.py b/src/home/bot/lang.py
index 2f10358..624c748 100644
--- a/src/home/bot/lang.py
+++ b/src/home/bot/lang.py
@@ -1,6 +1,8 @@
+from __future__ import annotations
+
import logging
-from typing import Union, Optional
+from typing import Union, Optional, List, Dict
logger = logging.getLogger(__name__)
@@ -24,7 +26,7 @@ class LangStrings(dict):
class LangPack:
- strings: dict[str, LangStrings[str, str]]
+ strings: Dict[str, LangStrings[str, str]]
default_lang: str
def __init__(self):
@@ -57,11 +59,14 @@ class LangPack:
return result
@property
- def languages(self) -> list[str]:
+ def languages(self) -> List[str]:
return list(self.strings.keys())
def get(self, key: str, lang: str, *args) -> str:
- return self.strings[lang][key] % args
+ if args:
+ return self.strings[lang][key] % args
+ else:
+ return self.strings[lang][key]
def __call__(self, *args, **kwargs):
return self.strings[self.default_lang][args[0]]
diff --git a/src/home/bot/wrapper.py b/src/home/bot/wrapper.py
index 8ebde4f..5f399ce 100644
--- a/src/home/bot/wrapper.py
+++ b/src/home/bot/wrapper.py
@@ -8,6 +8,7 @@ from telegram import (
ReplyKeyboardMarkup,
CallbackQuery,
User,
+ Message,
)
from telegram.ext import (
Updater,
@@ -22,7 +23,7 @@ from telegram.ext import (
)
from telegram.error import TimedOut
from ..config import config
-from typing import Optional, Union
+from typing import Optional, Union, List, Tuple
from .store import Store
from .lang import LangPack
from ..api.types import BotType
@@ -110,7 +111,7 @@ class Context:
kwargs = dict(parse_mode=ParseMode.HTML)
if not isinstance(markup, IgnoreMarkup):
kwargs['reply_markup'] = markup
- self._update.message.reply_text(text, **kwargs)
+ return self._update.message.reply_text(text, **kwargs)
def reply_exc(self, e: Exception) -> None:
self.reply(exc2text(e))
@@ -133,7 +134,7 @@ class Context:
return self._update.callback_query
@property
- def args(self) -> Optional[list[str]]:
+ def args(self) -> Optional[List[str]]:
return self._callback_context.args
@property
@@ -157,6 +158,25 @@ class Context:
return self._update.callback_query and self._update.callback_query.data and self._update.callback_query.data != ''
+def handlermethod(f: callable):
+ def _handler(self, update: Update, context: CallbackContext, *args, **kwargs):
+ ctx = Context(update,
+ callback_context=context,
+ markup_getter=self.markup,
+ lang=self.lang,
+ store=self.store)
+ try:
+ return f(self, ctx, *args, **kwargs)
+ except Exception as e:
+ if not self.exception_handler(e, ctx) and not isinstance(e, TimedOut):
+ logger.exception(e)
+ if not ctx.is_callback_context():
+ ctx.reply_exc(e)
+ else:
+ self.notify_user(ctx.user_id, exc2text(e))
+ return _handler
+
+
class Wrapper:
store: Optional[Store]
updater: Updater
@@ -252,7 +272,7 @@ class Wrapper:
def exception_handler(self, e: Exception, ctx: Context) -> Optional[bool]:
pass
- def notify_all(self, text_getter: callable, exclude: tuple[int] = ()) -> None:
+ def notify_all(self, text_getter: callable, exclude: Tuple[int] = ()) -> None:
if 'notify_users' not in config['bot']:
logger.error('notify_all() called but no notify_users directive found in the config')
return
@@ -280,6 +300,12 @@ class Wrapper:
def send_file(self, user_id, **kwargs):
self.updater.bot.send_document(chat_id=user_id, **kwargs)
+ def edit_message_text(self, user_id, message_id, *args, **kwargs):
+ self.updater.bot.edit_message_text(chat_id=user_id, message_id=message_id, parse_mode='HTML', *args, **kwargs)
+
+ def delete_message(self, user_id, message_id):
+ self.updater.bot.delete_message(chat_id=user_id, message_id=message_id)
+
#
# Language Selection
#
diff --git a/src/home/camera/util.py b/src/home/camera/util.py
index 39bfcd3..97f35aa 100644
--- a/src/home/camera/util.py
+++ b/src/home/camera/util.py
@@ -3,6 +3,7 @@ import os.path
import logging
import psutil
+from typing import List, Tuple
from ..util import chunks
from ..config import config
@@ -62,7 +63,7 @@ async def ffmpeg_cut(input: str,
_logger.info(f'ffmpeg_cut({input}): OK')
-def dvr_scan_timecodes(timecodes: str) -> list[tuple[int, int]]:
+def dvr_scan_timecodes(timecodes: str) -> List[Tuple[int, int]]:
tc_backup = timecodes
timecodes = timecodes.split(',')
diff --git a/src/home/database/bots.py b/src/home/database/bots.py
index bc490e1..99befc0 100644
--- a/src/home/database/bots.py
+++ b/src/home/database/bots.py
@@ -5,7 +5,7 @@ from ..api.types import (
BotType,
SoundSensorLocation
)
-from typing import Optional
+from typing import Optional, List, Tuple
from datetime import datetime
from html import escape
@@ -37,7 +37,7 @@ class BotsDatabase(MySQLDatabase):
self.commit()
def add_openwrt_logs(self,
- lines: list[tuple[datetime, str]]):
+ lines: List[Tuple[datetime, str]]):
now = datetime.now()
with self.cursor() as cursor:
for line in lines:
@@ -47,7 +47,7 @@ class BotsDatabase(MySQLDatabase):
self.commit()
def add_sound_hits(self,
- hits: list[tuple[SoundSensorLocation, int]],
+ hits: List[Tuple[SoundSensorLocation, int]],
time: datetime):
with self.cursor() as cursor:
for loc, count in hits:
@@ -58,7 +58,7 @@ class BotsDatabase(MySQLDatabase):
def get_sound_hits(self,
location: SoundSensorLocation,
after: Optional[datetime] = None,
- last: Optional[int] = None) -> list[dict]:
+ last: Optional[int] = None) -> List[dict]:
with self.cursor(dictionary=True) as cursor:
sql = "SELECT `time`, hits FROM sound_hits WHERE location=%s"
args = [location.name.lower()]
@@ -84,7 +84,7 @@ class BotsDatabase(MySQLDatabase):
def get_openwrt_logs(self,
filter_text: str,
min_id: int,
- limit: int = None) -> list[OpenwrtLogRecord]:
+ limit: int = None) -> List[OpenwrtLogRecord]:
tz = pytz.timezone('Europe/Moscow')
with self.cursor(dictionary=True) as cursor:
sql = "SELECT * FROM openwrt WHERE text LIKE %s AND id > %s"
diff --git a/src/home/media/node_client.py b/src/home/media/node_client.py
index 4430962..eb39898 100644
--- a/src/home/media/node_client.py
+++ b/src/home/media/node_client.py
@@ -2,7 +2,7 @@ import requests
import shutil
import logging
-from typing import Optional, Union
+from typing import Optional, Union, List
from .storage import RecordFile
from ..util import Addr
from ..api.errors import ApiResponseError
@@ -25,7 +25,7 @@ class MediaNodeClient:
def record_download(self, record_id: int, output: str):
return self._call(f'record/download/{record_id}/', save_to=output)
- def storage_list(self, extended=False, as_objects=False) -> Union[list[str], list[dict], list[RecordFile]]:
+ def storage_list(self, extended=False, as_objects=False) -> Union[List[str], List[dict], List[RecordFile]]:
r = self._call('storage/list/', params={'extended': int(extended)})
files = r['files']
if as_objects:
@@ -33,7 +33,7 @@ class MediaNodeClient:
return files
@staticmethod
- def record_list_from_serialized(files: Union[list[str], list[dict]]):
+ def record_list_from_serialized(files: Union[List[str], List[dict]]):
new_files = []
for f in files:
kwargs = {'remote': True}
diff --git a/src/home/media/record.py b/src/home/media/record.py
index fdb8382..cd7447a 100644
--- a/src/home/media/record.py
+++ b/src/home/media/record.py
@@ -5,7 +5,7 @@ import time
import subprocess
import signal
-from typing import Optional
+from typing import Optional, List, Dict
from ..util import find_child_processes, Addr
from ..config import config
from .storage import RecordFile, RecordStorage
@@ -22,7 +22,7 @@ class RecordHistoryItem:
request_time: float
start_time: float
stop_time: float
- relations: list[int]
+ relations: List[int]
status: RecordStatus
error: Optional[Exception]
file: Optional[RecordFile]
@@ -76,7 +76,7 @@ class RecordingNotFoundError(Exception):
class RecordHistory:
- history: dict[int, RecordHistoryItem]
+ history: Dict[int, RecordHistoryItem]
def __init__(self):
self.history = {}
diff --git a/src/home/media/record_client.py b/src/home/media/record_client.py
index f264155..322495c 100644
--- a/src/home/media/record_client.py
+++ b/src/home/media/record_client.py
@@ -7,7 +7,7 @@ from tempfile import gettempdir
from .record import RecordStatus
from .node_client import SoundNodeClient, MediaNodeClient, CameraNodeClient
from ..util import Addr
-from typing import Optional, Callable
+from typing import Optional, Callable, Dict
class RecordClient:
@@ -15,14 +15,14 @@ class RecordClient:
interrupted: bool
logger: logging.Logger
- clients: dict[str, MediaNodeClient]
- awaiting: dict[str, dict[int, Optional[dict]]]
+ clients: Dict[str, MediaNodeClient]
+ awaiting: Dict[str, Dict[int, Optional[dict]]]
error_handler: Optional[Callable]
finished_handler: Optional[Callable]
download_on_finish: bool
def __init__(self,
- nodes: dict[str, Addr],
+ nodes: Dict[str, Addr],
error_handler: Optional[Callable] = None,
finished_handler: Optional[Callable] = None,
download_on_finish=False):
@@ -50,7 +50,7 @@ class RecordClient:
self.stop()
self.logger.exception(exc)
- def make_clients(self, nodes: dict[str, Addr]):
+ def make_clients(self, nodes: Dict[str, Addr]):
pass
def stop(self):
@@ -148,9 +148,9 @@ class RecordClient:
class SoundRecordClient(RecordClient):
DOWNLOAD_EXTENSION = 'mp3'
- # clients: dict[str, SoundNodeClient]
+ # clients: Dict[str, SoundNodeClient]
- def make_clients(self, nodes: dict[str, Addr]):
+ def make_clients(self, nodes: Dict[str, Addr]):
for node, addr in nodes.items():
self.clients[node] = SoundNodeClient(addr)
self.awaiting[node] = {}
@@ -158,9 +158,9 @@ class SoundRecordClient(RecordClient):
class CameraRecordClient(RecordClient):
DOWNLOAD_EXTENSION = 'mp4'
- # clients: dict[str, CameraNodeClient]
+ # clients: Dict[str, CameraNodeClient]
- def make_clients(self, nodes: dict[str, Addr]):
+ def make_clients(self, nodes: Dict[str, Addr]):
for node, addr in nodes.items():
self.clients[node] = CameraNodeClient(addr)
self.awaiting[node] = {} \ No newline at end of file
diff --git a/src/home/media/storage.py b/src/home/media/storage.py
index 08ba06a..dd74ff8 100644
--- a/src/home/media/storage.py
+++ b/src/home/media/storage.py
@@ -3,7 +3,7 @@ import re
import shutil
import logging
-from typing import Optional, Union
+from typing import Optional, Union, List
from datetime import datetime
from ..util import strgen
@@ -149,7 +149,7 @@ class RecordStorage:
self.root = root
- def getfiles(self, as_objects=False) -> Union[list[str], list[RecordFile]]:
+ def getfiles(self, as_objects=False) -> Union[List[str], List[RecordFile]]:
files = []
for name in os.listdir(self.root):
path = os.path.join(self.root, name)
diff --git a/src/home/telegram/telegram.py b/src/home/telegram/telegram.py
index 9c7ea73..2f94f93 100644
--- a/src/home/telegram/telegram.py
+++ b/src/home/telegram/telegram.py
@@ -1,6 +1,7 @@
import requests
import logging
+from typing import Tuple
from ..config import config
@@ -29,7 +30,7 @@ def send_photo(filename: str):
def _send_telegram_data(text: str,
parse_mode: str = None,
- disable_web_page_preview: bool = False) -> tuple[dict, str]:
+ disable_web_page_preview: bool = False) -> Tuple[dict, str]:
data = {
'chat_id': config['telegram']['chat_id'],
'text': text
diff --git a/src/home/util.py b/src/home/util.py
index 9dd84f6..5050ebb 100644
--- a/src/home/util.py
+++ b/src/home/util.py
@@ -9,7 +9,7 @@ import random
from enum import Enum
from datetime import datetime
-from typing import Tuple, Optional
+from typing import Tuple, Optional, List
Addr = Tuple[str, int] # network address type (host, port)
@@ -96,7 +96,7 @@ def send_datagram(message: str, addr: Addr) -> None:
sock.sendto(message.encode(), addr)
-def format_tb(exc) -> Optional[list[str]]:
+def format_tb(exc) -> Optional[List[str]]:
tb = traceback.format_tb(exc.__traceback__)
if not tb:
return None
@@ -120,7 +120,7 @@ class ChildProcessInfo:
self.cmd = cmd
-def find_child_processes(ppid: int) -> list[ChildProcessInfo]:
+def find_child_processes(ppid: int) -> List[ChildProcessInfo]:
p = subprocess.run(['pgrep', '-P', str(ppid), '--list-full'], capture_output=True)
if p.returncode != 0:
raise OSError(f'pgrep returned {p.returncode}')