mirror of
https://github.com/paul-nameless/tg
synced 2024-11-22 03:43:19 +00:00
Add ability to delete chat (#145)
* Add ability to delete chat * Leave chat
This commit is contained in:
parent
fbc3e5d07a
commit
0e13cc1cc1
5 changed files with 118 additions and 33 deletions
|
@ -18,13 +18,14 @@ Telegram terminal client.
|
|||
- [X] auto download files
|
||||
- [X] toggle chats: pin/unpin, mark as read/unread, mute/unmute
|
||||
- [X] message history
|
||||
- [X] list contacts
|
||||
- [X] show user status
|
||||
|
||||
TODO:
|
||||
|
||||
- [ ] secret chats
|
||||
- [ ] list contacts
|
||||
- [ ] search
|
||||
- [ ] show users and their status in current chat
|
||||
- [ ] show members in chat
|
||||
- [ ] create new chat
|
||||
- [ ] bots (bot keyboard)
|
||||
|
||||
|
@ -213,6 +214,7 @@ For navigation arrow keys also can be used.
|
|||
- `u`: mark read/unread
|
||||
- `r`: read current chat
|
||||
- `c`: show list of contacts
|
||||
- `dd`: delete chat or remove history
|
||||
- `?`: show help
|
||||
|
||||
## Msgs:
|
||||
|
|
|
@ -13,12 +13,13 @@ from telegram.utils import AsyncResult
|
|||
from tg import config
|
||||
from tg.models import Model
|
||||
from tg.msg import MsgProxy
|
||||
from tg.tdlib import ChatAction, Tdlib
|
||||
from tg.tdlib import ChatAction, ChatType, Tdlib, get_chat_type
|
||||
from tg.utils import (
|
||||
get_duration,
|
||||
get_mime,
|
||||
get_video_resolution,
|
||||
get_waveform,
|
||||
is_no,
|
||||
is_yes,
|
||||
notify,
|
||||
suspend,
|
||||
|
@ -337,6 +338,8 @@ class Controller:
|
|||
f"Upload <{file_path}> compressed?[Y/n]"
|
||||
)
|
||||
self.render_status()
|
||||
if resp is None:
|
||||
return self.present_info("Uploading cancelled")
|
||||
if not is_yes(resp):
|
||||
mime = ""
|
||||
|
||||
|
@ -377,7 +380,10 @@ class Controller:
|
|||
def send_file(
|
||||
self, send_file_fun: Callable[[str, int], AsyncResult],
|
||||
) -> None:
|
||||
file_path = os.path.expanduser(self.view.status.get_input())
|
||||
_input = self.view.status.get_input()
|
||||
if _input is None:
|
||||
return
|
||||
file_path = os.path.expanduser(_input)
|
||||
if not file_path or not os.path.isfile(file_path):
|
||||
return self.present_info("Given path to file does not exist")
|
||||
|
||||
|
@ -397,13 +403,11 @@ class Controller:
|
|||
resp = self.view.status.get_input(
|
||||
f"Do you want to send recording: {file_path}? [Y/n]"
|
||||
)
|
||||
if not is_yes(resp):
|
||||
self.present_info("Voice message discarded")
|
||||
return
|
||||
if resp is None or not is_yes(resp):
|
||||
return self.present_info("Voice message discarded")
|
||||
|
||||
if not os.path.isfile(file_path):
|
||||
self.present_info(f"Can't load recording file {file_path}")
|
||||
return
|
||||
return self.present_info(f"Can't load recording file {file_path}")
|
||||
|
||||
chat_id = self.model.chats.id_by_index(self.model.current_chat)
|
||||
if not chat_id:
|
||||
|
@ -495,6 +499,47 @@ class Controller:
|
|||
self.model.edit_message(text=text)
|
||||
self.present_info("Message edited")
|
||||
|
||||
@bind(chat_handler, ["dd"])
|
||||
def delete_chat(self) -> None:
|
||||
"""Leave group/channel or delete private/secret chats"""
|
||||
|
||||
chat = self.model.chats.chats[self.model.current_chat]
|
||||
chat_type = get_chat_type(chat)
|
||||
if chat_type in (
|
||||
ChatType.chatTypeSupergroup,
|
||||
ChatType.chatTypeBasicGroup,
|
||||
ChatType.channel,
|
||||
):
|
||||
resp = self.view.status.get_input(
|
||||
"Are you sure you want to leave this group/channel?[y/N]"
|
||||
)
|
||||
if is_no(resp or ""):
|
||||
return self.present_info("Not leaving group/channel")
|
||||
self.tg.leave_chat(chat["id"])
|
||||
self.tg.delete_chat_history(
|
||||
chat["id"], remove_from_chat_list=True, revoke=False
|
||||
)
|
||||
return
|
||||
|
||||
resp = self.view.status.get_input(
|
||||
"Are you sure you want to delete the chat?[y/N]"
|
||||
)
|
||||
if is_no(resp or ""):
|
||||
return self.present_info("Not deleting chat")
|
||||
|
||||
is_revoke = False
|
||||
if chat["can_be_deleted_for_all_users"]:
|
||||
resp = self.view.status.get_input("Delete for all users?[y/N]")
|
||||
if resp is None:
|
||||
return self.present_info("Not deleting chat")
|
||||
self.render_status()
|
||||
is_revoke = is_no(resp)
|
||||
|
||||
self.tg.delete_chat_history(
|
||||
chat["id"], remove_from_chat_list=True, revoke=is_revoke
|
||||
)
|
||||
self.present_info("Chat was deleted")
|
||||
|
||||
@bind(chat_handler, ["n"])
|
||||
def next_found_chat(self) -> None:
|
||||
"""Go to next found chat"""
|
||||
|
|
35
tg/tdlib.py
35
tg/tdlib.py
|
@ -303,6 +303,41 @@ class Tdlib(Telegram):
|
|||
}
|
||||
return self._send_data(data)
|
||||
|
||||
def leave_chat(self, chat_id: int) -> AsyncResult:
|
||||
data = {
|
||||
"@type": "leaveChat",
|
||||
"chat_id": chat_id,
|
||||
}
|
||||
return self._send_data(data)
|
||||
|
||||
def join_chat(self, chat_id: int) -> AsyncResult:
|
||||
data = {
|
||||
"@type": "joinChat",
|
||||
"chat_id": chat_id,
|
||||
}
|
||||
return self._send_data(data)
|
||||
|
||||
def close_secret_chat(self, secret_chat_id: int) -> AsyncResult:
|
||||
data = {
|
||||
"@type": "closeSecretChat",
|
||||
"secret_chat_id": secret_chat_id,
|
||||
}
|
||||
return self._send_data(data)
|
||||
|
||||
def delete_chat_history(
|
||||
self, chat_id: int, remove_from_chat_list: bool, revoke: bool = False
|
||||
) -> AsyncResult:
|
||||
"""
|
||||
revoke: Pass true to try to delete chat history for all users
|
||||
"""
|
||||
data = {
|
||||
"@type": "deleteChatHistory",
|
||||
"chat_id": chat_id,
|
||||
"remove_from_chat_list": remove_from_chat_list,
|
||||
"revoke": revoke,
|
||||
}
|
||||
return self._send_data(data)
|
||||
|
||||
|
||||
def get_chat_type(chat: Dict[str, Any]) -> Optional[ChatType]:
|
||||
try:
|
||||
|
|
|
@ -127,9 +127,11 @@ def num(value: str, default: Optional[int] = None) -> Optional[int]:
|
|||
|
||||
|
||||
def is_yes(resp: str) -> bool:
|
||||
if resp.strip().lower() == "y" or resp == "":
|
||||
return True
|
||||
return False
|
||||
return not resp or resp.strip().lower() == "y"
|
||||
|
||||
|
||||
def is_no(resp: str) -> bool:
|
||||
return not resp or resp.strip().lower() == "n"
|
||||
|
||||
|
||||
def get_duration(file_path: str) -> int:
|
||||
|
|
43
tg/views.py
43
tg/views.py
|
@ -95,31 +95,32 @@ class StatusView:
|
|||
self.win.addstr(0, 0, msg.replace("\n", " ")[: self.w])
|
||||
self._refresh()
|
||||
|
||||
def get_input(self, buff: str = "", prefix: str = "") -> str:
|
||||
def get_input(self, buff: str = "", prefix: str = "") -> Optional[str]:
|
||||
curses.curs_set(1)
|
||||
|
||||
while True:
|
||||
self.win.erase()
|
||||
line = buff[-(self.w - 1) :]
|
||||
self.win.addstr(0, 0, f"{prefix}{line}")
|
||||
try:
|
||||
while True:
|
||||
self.win.erase()
|
||||
line = buff[-(self.w - 1) :]
|
||||
self.win.addstr(0, 0, f"{prefix}{line}")
|
||||
|
||||
key = self.win.get_wch(0, min(len(buff + prefix), self.w - 1))
|
||||
key = ord(key)
|
||||
if key == 10: # return
|
||||
break
|
||||
elif key == 127: # del
|
||||
if buff:
|
||||
buff = buff[:-1]
|
||||
elif key in (7, 27): # (^G, <esc>) cancel
|
||||
buff = ""
|
||||
break
|
||||
elif chr(key).isprintable():
|
||||
buff += chr(key)
|
||||
key = self.win.get_wch(0, min(len(buff + prefix), self.w - 1))
|
||||
key = ord(key)
|
||||
if key == 10: # return
|
||||
break
|
||||
elif key == 127: # del
|
||||
if buff:
|
||||
buff = buff[:-1]
|
||||
elif key in (7, 27): # (^G, <esc>) cancel
|
||||
return None
|
||||
elif chr(key).isprintable():
|
||||
buff += chr(key)
|
||||
finally:
|
||||
self.win.clear()
|
||||
curses.curs_set(0)
|
||||
curses.cbreak()
|
||||
curses.noecho()
|
||||
|
||||
self.win.clear()
|
||||
curses.curs_set(0)
|
||||
curses.cbreak()
|
||||
curses.noecho()
|
||||
return buff
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue