mirror of
https://github.com/paul-nameless/tg
synced 2024-11-22 03:43:19 +00:00
File picker (#136)
* File picker * Add docstring to show in help * Update doc * Fix update_file: invalid get msg * Catch ValueError when removing messages * Ask to whether to upload file as document or compressed * Clear status after sending file
This commit is contained in:
parent
39a1e59f35
commit
b37bba8fb7
7 changed files with 89 additions and 38 deletions
|
@ -75,6 +75,7 @@ yay -S telegram-tg-git
|
|||
```ini
|
||||
image/webp; mpv %s
|
||||
```
|
||||
- [ranger](https://github.com/ranger/ranger), [nnn](https://github.com/jarun/nnn) - can be used to choose file when sending, customizable with `FILE_PICKER_CMD`
|
||||
|
||||
|
||||
## Configuration
|
||||
|
@ -165,6 +166,9 @@ USERS_COLORS = tuple(range(2, 16))
|
|||
# cleanup cache
|
||||
# Values: N days, None (never)
|
||||
KEEP_MEDIA = 7
|
||||
|
||||
FILE_PICKER_CMD = "ranger --choosefile={file_path}"
|
||||
# FILE_PICKER_CMD = "nnn -p {file_path}"
|
||||
```
|
||||
|
||||
### Mailcap file
|
||||
|
|
|
@ -71,6 +71,8 @@ USERS_COLORS = tuple(range(2, 16))
|
|||
|
||||
KEEP_MEDIA = 7
|
||||
|
||||
FILE_PICKER_CMD = "ranger --choosefile={file_path}"
|
||||
|
||||
|
||||
if os.path.isfile(CONFIG_FILE):
|
||||
config_params = runpy.run_path(CONFIG_FILE)
|
||||
|
|
|
@ -15,6 +15,7 @@ from tg.msg import MsgProxy
|
|||
from tg.tdlib import ChatAction, Tdlib
|
||||
from tg.utils import (
|
||||
get_duration,
|
||||
get_mime,
|
||||
get_video_resolution,
|
||||
get_waveform,
|
||||
is_yes,
|
||||
|
@ -299,18 +300,6 @@ class Controller:
|
|||
)
|
||||
self.present_info("Message wasn't sent")
|
||||
|
||||
@bind(msg_handler, ["sv"])
|
||||
def send_video(self) -> None:
|
||||
file_path = self.view.status.get_input()
|
||||
if not file_path or not os.path.isfile(file_path):
|
||||
return
|
||||
chat_id = self.model.chats.id_by_index(self.model.current_chat)
|
||||
if not chat_id:
|
||||
return
|
||||
width, height = get_video_resolution(file_path)
|
||||
duration = get_duration(file_path)
|
||||
self.tg.send_video(file_path, chat_id, width, height, duration)
|
||||
|
||||
@bind(msg_handler, ["dd"])
|
||||
def delete_msgs(self) -> None:
|
||||
is_deleted = self.model.delete_msgs()
|
||||
|
@ -320,18 +309,70 @@ class Controller:
|
|||
return
|
||||
self.present_info("Message deleted")
|
||||
|
||||
@bind(msg_handler, ["S"])
|
||||
def choose_and_send_file(self) -> None:
|
||||
"""Call file picker and send chosen file based on mimetype"""
|
||||
chat_id = self.model.chats.id_by_index(self.model.current_chat)
|
||||
file_path = None
|
||||
if not chat_id:
|
||||
return self.present_error("No chat selected")
|
||||
try:
|
||||
with NamedTemporaryFile("w") as f, suspend(self.view) as s:
|
||||
s.call(config.FILE_PICKER_CMD.format(file_path=f.name))
|
||||
with open(f.name) as f:
|
||||
file_path = f.read().strip()
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
if not file_path or not os.path.isfile(file_path):
|
||||
return self.present_error("No file was selected")
|
||||
mime_map = {
|
||||
"image": self.tg.send_photo,
|
||||
"audio": self.tg.send_audio,
|
||||
"video": self._send_video,
|
||||
}
|
||||
mime = get_mime(file_path)
|
||||
if mime in ("image", "video"):
|
||||
resp = self.view.status.get_input(
|
||||
f"Upload <{file_path}> compressed?[Y/n]"
|
||||
)
|
||||
self.render_status()
|
||||
if not is_yes(resp):
|
||||
mime = ""
|
||||
|
||||
fun = mime_map.get(mime, self.tg.send_doc)
|
||||
fun(file_path, chat_id)
|
||||
|
||||
@bind(msg_handler, ["sd"])
|
||||
def send_document(self) -> None:
|
||||
"""Enter file path and send uncompressed"""
|
||||
self.send_file(self.tg.send_doc)
|
||||
|
||||
@bind(msg_handler, ["sp"])
|
||||
def send_picture(self) -> None:
|
||||
"""Enter file path and send compressed image"""
|
||||
self.send_file(self.tg.send_photo)
|
||||
|
||||
@bind(msg_handler, ["sa"])
|
||||
def send_audio(self) -> None:
|
||||
"""Enter file path and send as audio"""
|
||||
self.send_file(self.tg.send_audio)
|
||||
|
||||
@bind(msg_handler, ["sv"])
|
||||
def send_video(self) -> None:
|
||||
"""Enter file path and send compressed video"""
|
||||
file_path = self.view.status.get_input()
|
||||
if not file_path or not os.path.isfile(file_path):
|
||||
return
|
||||
chat_id = self.model.chats.id_by_index(self.model.current_chat)
|
||||
if not chat_id:
|
||||
return
|
||||
self._send_video(file_path, chat_id)
|
||||
|
||||
def _send_video(self, file_path: str, chat_id: int) -> None:
|
||||
width, height = get_video_resolution(file_path)
|
||||
duration = get_duration(file_path)
|
||||
self.tg.send_video(file_path, chat_id, width, height, duration)
|
||||
|
||||
def send_file(
|
||||
self, send_file_fun: Callable[[str, int], AsyncResult],
|
||||
) -> None:
|
||||
|
@ -627,8 +668,14 @@ class Controller:
|
|||
self.queue.put(self._render)
|
||||
|
||||
def _render(self) -> None:
|
||||
self.render_chats()
|
||||
self.render_msgs()
|
||||
self._render_chats()
|
||||
self._render_msgs()
|
||||
self._render_status()
|
||||
|
||||
def render_status(self) -> None:
|
||||
self.queue.put(self._render_status)
|
||||
|
||||
def _render_status(self) -> None:
|
||||
self.view.status.draw()
|
||||
|
||||
def render_chats(self) -> None:
|
||||
|
|
|
@ -362,7 +362,10 @@ class MsgModel:
|
|||
def remove_messages(self, chat_id: int, msg_ids: List[int]) -> None:
|
||||
log.info(f"removing msg {msg_ids=}")
|
||||
for msg_id in msg_ids:
|
||||
try:
|
||||
self.msg_ids[chat_id].remove(msg_id)
|
||||
except ValueError:
|
||||
pass
|
||||
self.msgs[chat_id].pop(msg_id, None)
|
||||
|
||||
def add_message(self, chat_id: int, msg: Dict[str, Any]) -> None:
|
||||
|
|
|
@ -229,20 +229,19 @@ def update_file(controller: Controller, update: Dict[str, Any]) -> None:
|
|||
file_id = update["file"]["id"]
|
||||
local = update["file"]["local"]
|
||||
chat_id, msg_id = controller.model.downloads.get(file_id, (None, None))
|
||||
if chat_id is None:
|
||||
if chat_id is None or msg_id is None:
|
||||
log.warning(
|
||||
"Can't find information about file with file_id=%s", file_id
|
||||
)
|
||||
return
|
||||
msgs = controller.model.msgs.msgs[chat_id]
|
||||
for msg_id, msg in msgs.items():
|
||||
if msg["id"] == msg_id:
|
||||
msg = controller.model.msgs.msgs[chat_id].get(msg_id)
|
||||
if not msg:
|
||||
return
|
||||
proxy = MsgProxy(msg)
|
||||
proxy.local = local
|
||||
controller.render_msgs()
|
||||
if proxy.is_downloaded:
|
||||
controller.model.downloads.pop(file_id)
|
||||
break
|
||||
|
||||
|
||||
@update_handler("updateMessageContentOpened")
|
||||
|
|
|
@ -72,6 +72,13 @@ def setup_log() -> None:
|
|||
logging.captureWarnings(True)
|
||||
|
||||
|
||||
def get_mime(file_path: str) -> str:
|
||||
mtype, _ = mimetypes.guess_type(file_path)
|
||||
if not mtype:
|
||||
return ""
|
||||
return mtype.split("/")[0]
|
||||
|
||||
|
||||
def get_file_handler(file_path: str) -> str:
|
||||
mtype, _ = mimetypes.guess_type(file_path)
|
||||
if not mtype:
|
||||
|
|
15
tg/views.py
15
tg/views.py
|
@ -5,16 +5,7 @@ from datetime import datetime
|
|||
from typing import Any, Dict, List, Optional, Tuple, Union, cast
|
||||
|
||||
from tg import config
|
||||
from tg.colors import (
|
||||
blue,
|
||||
bold,
|
||||
cyan,
|
||||
get_color,
|
||||
magenta,
|
||||
reverse,
|
||||
white,
|
||||
yellow,
|
||||
)
|
||||
from tg.colors import bold, cyan, get_color, magenta, reverse, white, yellow
|
||||
from tg.models import Model, UserModel
|
||||
from tg.msg import MsgProxy
|
||||
from tg.tdlib import ChatType, get_chat_type
|
||||
|
@ -98,10 +89,8 @@ class StatusView:
|
|||
self.win.resize(self.h, self.w)
|
||||
self.win.mvwin(self.y, self.x)
|
||||
|
||||
def draw(self, msg: Optional[str] = None) -> None:
|
||||
def draw(self, msg: str = "") -> None:
|
||||
self.win.clear()
|
||||
if not msg:
|
||||
return
|
||||
self.win.addstr(0, 0, msg.replace("\n", " ")[: self.w])
|
||||
self._refresh()
|
||||
|
||||
|
|
Loading…
Reference in a new issue