mirror of
https://github.com/paul-nameless/tg
synced 2024-11-24 21:03:06 +00:00
Merge pull request #111 from paul-nameless/custom-cmd
Allow to open current msg with custom cmd
This commit is contained in:
commit
73d3e535bb
4 changed files with 52 additions and 23 deletions
|
@ -230,3 +230,4 @@ For navigation arrow keys also can be used.
|
|||
- `]`: next chat
|
||||
- `[`: prev chat
|
||||
- `?`: show help
|
||||
- `!`: open msg with custom cmd
|
||||
|
|
|
@ -392,15 +392,14 @@ class Controller:
|
|||
chat = self.model.chats.chats[self.model.current_chat]
|
||||
return chat["permissions"]["can_send_messages"]
|
||||
|
||||
@bind(msg_handler, ["l", "^J"])
|
||||
def open_current_msg(self) -> None:
|
||||
msg = MsgProxy(self.model.current_msg)
|
||||
def _open_msg(self, msg: MsgProxy, cmd: str = None) -> None:
|
||||
if msg.is_text:
|
||||
with NamedTemporaryFile("w", suffix=".txt") as f:
|
||||
f.write(msg.text_content)
|
||||
f.flush()
|
||||
with suspend(self.view) as s:
|
||||
s.open_file(f.name)
|
||||
if err_msg := s.open_file(f.name, cmd):
|
||||
self.present_error(err_msg)
|
||||
return
|
||||
|
||||
path = msg.local_path
|
||||
|
@ -412,7 +411,21 @@ class Controller:
|
|||
return
|
||||
self.tg.open_message_content(chat_id, msg.msg_id)
|
||||
with suspend(self.view) as s:
|
||||
s.open_file(path)
|
||||
if err_msg := s.open_file(path, cmd):
|
||||
self.present_error(err_msg)
|
||||
|
||||
@bind(msg_handler, ["!"])
|
||||
def open_msg_with_cmd(self) -> None:
|
||||
"""Open msg or file with cmd: less %s"""
|
||||
msg = MsgProxy(self.model.current_msg)
|
||||
if cmd := self.view.status.get_input():
|
||||
return self._open_msg(msg, cmd)
|
||||
|
||||
@bind(msg_handler, ["l", "^J"])
|
||||
def open_current_msg(self) -> None:
|
||||
"""Open msg or file with cmd in mailcap"""
|
||||
msg = MsgProxy(self.model.current_msg)
|
||||
self._open_msg(msg)
|
||||
|
||||
@bind(msg_handler, ["e"])
|
||||
def edit_msg(self) -> None:
|
||||
|
@ -526,13 +539,16 @@ class Controller:
|
|||
self.resize()
|
||||
|
||||
while True:
|
||||
repeat_factor, keys = self.view.get_keys()
|
||||
fun = handlers.get(keys, lambda *_: None)
|
||||
res = fun(self, repeat_factor) # type: ignore
|
||||
if res == "QUIT":
|
||||
return res
|
||||
elif res == "BACK":
|
||||
return res
|
||||
try:
|
||||
repeat_factor, keys = self.view.get_keys()
|
||||
fun = handlers.get(keys, lambda *_: None)
|
||||
res = fun(self, repeat_factor) # type: ignore
|
||||
if res == "QUIT":
|
||||
return res
|
||||
elif res == "BACK":
|
||||
return res
|
||||
except Exception:
|
||||
log.exception("Error happend in key handle loop")
|
||||
|
||||
def resize_handler(self, signum: int, frame: Any) -> None:
|
||||
curses.endwin()
|
||||
|
|
32
tg/utils.py
32
tg/utils.py
|
@ -15,6 +15,7 @@ import sys
|
|||
from datetime import datetime
|
||||
from functools import lru_cache
|
||||
from logging.handlers import RotatingFileHandler
|
||||
from subprocess import CompletedProcess
|
||||
from types import TracebackType
|
||||
from typing import Any, Optional, Tuple, Type
|
||||
|
||||
|
@ -63,7 +64,7 @@ def setup_log() -> None:
|
|||
handlers.append(handler)
|
||||
|
||||
logging.basicConfig(
|
||||
format="%(levelname)-8s [%(asctime)s] %(name)s %(message)s",
|
||||
format="%(levelname)s [%(asctime)s] %(filename)s:%(lineno)s - %(funcName)s | %(message)s",
|
||||
handlers=handlers,
|
||||
)
|
||||
logging.getLogger().setLevel(config.LOG_LEVEL)
|
||||
|
@ -71,10 +72,11 @@ def setup_log() -> None:
|
|||
logging.captureWarnings(True)
|
||||
|
||||
|
||||
def get_file_handler(file_path: str, default: str = None) -> Optional[str]:
|
||||
def get_file_handler(file_path: str) -> str:
|
||||
mtype, _ = mimetypes.guess_type(file_path)
|
||||
if not mtype:
|
||||
return default
|
||||
return config.DEFAULT_OPEN.format(file_path=shlex.quote(file_path))
|
||||
|
||||
caps = mailcap.getcaps()
|
||||
handler, view = mailcap.findmatch(caps, mtype, filename=file_path)
|
||||
if not handler:
|
||||
|
@ -181,17 +183,27 @@ class suspend:
|
|||
def __init__(self, view: Any) -> None:
|
||||
self.view = view
|
||||
|
||||
def call(self, cmd: str) -> None:
|
||||
subprocess.call(cmd, shell=True)
|
||||
def call(self, cmd: str) -> CompletedProcess:
|
||||
return subprocess.run(cmd, shell=True, stderr=subprocess.PIPE)
|
||||
|
||||
def run_with_input(self, cmd: str, text: str) -> None:
|
||||
subprocess.run(cmd, universal_newlines=True, input=text, shell=True)
|
||||
|
||||
def open_file(self, file_path: str) -> None:
|
||||
cmd = get_file_handler(file_path)
|
||||
if not cmd:
|
||||
return
|
||||
self.call(cmd)
|
||||
def open_file(self, file_path: str, cmd: str = None) -> str:
|
||||
if cmd:
|
||||
try:
|
||||
cmd = cmd % shlex.quote(file_path)
|
||||
except TypeError:
|
||||
return "command should contain <%s> which will be replaced by file path"
|
||||
else:
|
||||
cmd = get_file_handler(file_path)
|
||||
|
||||
proc = self.call(cmd)
|
||||
if proc.returncode:
|
||||
stderr = proc.stderr.decode()
|
||||
log.error("Error happened executing <%s>:\n%s", cmd, stderr)
|
||||
return stderr
|
||||
return ""
|
||||
|
||||
def __enter__(self) -> "suspend":
|
||||
for view in (self.view.chats, self.view.msgs, self.view.status):
|
||||
|
|
|
@ -102,7 +102,7 @@ class StatusView:
|
|||
self.win.clear()
|
||||
if not msg:
|
||||
return
|
||||
self.win.addstr(0, 0, msg[: self.w])
|
||||
self.win.addstr(0, 0, msg.replace("\n", " ")[: self.w])
|
||||
self._refresh()
|
||||
|
||||
def get_input(self, msg: str = "") -> str:
|
||||
|
|
Loading…
Reference in a new issue