Merge branch 'master' into quick-fix-notification

This commit is contained in:
Alexander Zveruk 2020-05-16 12:35:18 +03:00
commit f17a42bad2
3 changed files with 143 additions and 39 deletions

View file

@ -1,9 +1,9 @@
import logging
from typing import Dict, Any, Optional
import os
import threading
from datetime import datetime
from tempfile import NamedTemporaryFile
from typing import Any, Dict, Optional
from telegram.client import Telegram
@ -47,17 +47,18 @@ class Controller:
self.lock = threading.Lock()
self.tg = tg
self.handlers = {
"updateNewMessage": self.update_new_msg,
"updateMessageContent": self.update_msg_content,
"updateChatIsPinned": self.update_chat_is_pinned,
"updateChatDraftMessage": self.update_chat_draft_msg,
"updateChatIsMarkedAsUnread": self.update_chat_marked_as_unread,
"updateChatIsPinned": self.update_chat_is_pinned,
"updateChatLastMessage": self.update_chat_last_msg,
"updateChatNotificationSettings": self.update_chat_notification_settings,
"updateChatOrder": self.update_chat_order,
"updateChatReadInbox": self.update_chat_read_inbox,
"updateChatTitle": self.update_chat_title,
"updateChatLastMessage": self.update_chat_last_msg,
"updateChatDraftMessage": self.update_chat_draft_msg,
"updateChatOrder": self.update_chat_order,
"updateMessageSendSucceeded": self.update_msg_send_succeeded,
"updateFile": self.update_file,
"updateMessageContent": self.update_msg_content,
"updateMessageSendSucceeded": self.update_msg_send_succeeded,
"updateNewMessage": self.update_new_msg,
}
def send_file(self, send_file_fun, *args, **kwargs):
@ -258,6 +259,39 @@ class Controller:
with suspend(self.view):
breakpoint()
elif keys == "u":
chat = self.model.chats.chats[self.model.current_chat]
chat_id = chat["id"]
toggle = not chat["is_marked_as_unread"]
self.tg.toggle_chat_is_marked_as_unread(chat_id, toggle)
elif keys == "p":
chat = self.model.chats.chats[self.model.current_chat]
chat_id = chat["id"]
toggle = not chat["is_pinned"]
self.tg.toggle_chat_is_pinned(chat_id, toggle)
elif keys == "r":
chat = self.model.chats.chats[self.model.current_chat]
chat_id = chat["id"]
msg_id = chat["last_message"]["id"]
self.tg.view_messages(chat_id, [msg_id])
elif keys == "m":
# TODO: if it's msg to yourself, do not change its
# notification setting, because we can't by documentation,
# instead write about it in status
chat = self.model.chats.chats[self.model.current_chat]
chat_id = chat["id"]
notification_settings = chat["notification_settings"]
if notification_settings["mute_for"]:
notification_settings["mute_for"] = 0
else:
notification_settings["mute_for"] = 2147483647
self.tg.set_chat_nottification_settings(
chat_id, notification_settings
)
def refresh_chats(self) -> None:
with self.lock:
# using lock here, because refresh_chats is used from another
@ -424,6 +458,16 @@ class Controller:
break
self.refresh_chats()
@handle_exception
def update_chat_notification_settings(self, update):
log.info("Proccessing update_chat_notification_settings")
chat_id = update["chat_id"]
notification_settings = update["notification_settings"]
self.model.chats.update_chat(
chat_id, notification_settings=notification_settings
)
self.refresh_chats()
@handle_exception
def update_msg_send_succeeded(self, update):
chat_id = update["message"]["chat_id"]

View file

@ -106,6 +106,45 @@ class TelegramApi(Telegram):
}
return self._send_data(data)
def toggle_chat_is_marked_as_unread(
self, chat_id: int, is_marked_as_unread: bool
):
data = {
"@type": "toggleChatIsMarkedAsUnread",
"chat_id": chat_id,
"is_marked_as_unread": is_marked_as_unread,
}
return self._send_data(data)
def toggle_chat_is_pinned(self, chat_id: int, is_pinned: bool):
data = {
"@type": "toggleChatIsPinned",
"chat_id": chat_id,
"is_pinned": is_pinned,
}
return self._send_data(data)
def set_chat_nottification_settings(
self, chat_id: int, notification_settings: dict
):
data = {
"@type": "setChatNotificationSettings",
"chat_id": chat_id,
"notification_settings": notification_settings,
}
return self._send_data(data)
def view_messages(
self, chat_id: int, message_ids: list, force_read: bool = True
):
data = {
"@type": "viewMessages",
"chat_id": chat_id,
"message_ids": message_ids,
"force_read": force_read,
}
return self._send_data(data)
def main():
def signal_handler(sig, frame):
@ -130,6 +169,7 @@ def main():
)
config.record_cmd = cfg.get("record_cmd")
tg.login()
wrapper(partial(run, tg))

View file

@ -1,13 +1,12 @@
import curses
import logging
import re
from _curses import window
from datetime import datetime
from typing import Any, Dict, List, Optional, Tuple, Iterator
from typing import Any, Dict, List, Optional, Tuple
from tg.colors import blue, cyan, get_color, magenta, reverse, white
from tg.msg import MsgProxy
from tg.utils import num, truncate_to_len, emoji_pattern
from tg.utils import emoji_pattern, num, truncate_to_len
log = logging.getLogger(__name__)
@ -25,13 +24,14 @@ MULTICHAR_KEYBINDINGS = (
class View:
def __init__(self, stdscr: window) -> None:
curses.start_color()
curses.noecho()
curses.cbreak()
stdscr.keypad(True)
curses.curs_set(0)
curses.start_color()
curses.use_default_colors()
# init white color first to initialize colors correctly
get_color(white, -1)
self.stdscr = stdscr
self.chats = ChatView(stdscr)
@ -129,32 +129,43 @@ class ChatView:
self.win.resize(self.h, self.w)
def _msg_color(self, is_selected: bool = False) -> int:
return get_color(white, -1) | (reverse if is_selected else 0)
color = get_color(white, -1)
if is_selected:
return color | reverse
return color
def _unread_color(self, is_selected: bool = False) -> int:
return get_color(magenta, -1) | (reverse if is_selected else 0)
color = get_color(magenta, -1)
if is_selected:
return color | reverse
return color
def _msg_attribures(self, is_selected: bool = False) -> List[int]:
return map(
lambda x: x | (reverse if is_selected else 0),
[get_color(cyan, -1), get_color(blue, -1), self._msg_color(),],
def _chat_attributes(self, is_selected: bool = False) -> Tuple[int]:
attrs = (
get_color(cyan, -1),
get_color(blue, -1),
self._msg_color(is_selected),
)
if is_selected:
return tuple(attr | reverse for attr in attrs)
return attrs
def draw(self, current: int, chats: List[Dict[str, Any]]) -> None:
self.win.erase()
self.win.vline(0, self.w - 1, curses.ACS_VLINE, self.h)
for i, chat in enumerate(chats):
is_selected = i == current
unread_count = (
chat["unread_count"] or 1 if chat["is_marked_as_unread"] else 0
)
unread_count = chat["unread_count"]
if chat["is_marked_as_unread"]:
unread_count = "unread"
date = get_date(chat)
title = chat["title"]
is_pinned = chat["is_pinned"]
last_msg = get_last_msg(chat)
offset = 0
for attr, elem in zip(
self._msg_attribures(is_selected), [f"{date} ", title]
self._chat_attributes(is_selected), [f"{date} ", title]
):
if offset > self.w:
break
@ -167,12 +178,14 @@ class ChatView:
offset += len(elem)
if offset >= self.w:
continue
break
last_msg = " " + last_msg.replace("\n", " ")
last_msg = truncate_to_len(last_msg, self.w - offset - 1)
self.win.addstr(i, offset, last_msg, self._msg_color(is_selected))
last_msg = truncate_to_len(last_msg, self.w - offset)
if last_msg.strip():
self.win.addstr(
i, offset, last_msg, self._msg_color(is_selected)
)
if left_label := self._get_chat_label(
unread_count, is_pinned, chat
@ -190,16 +203,20 @@ class ChatView:
def _get_chat_label(
unread_count: int, is_pinned: bool, chat: Dict[str, Any]
) -> str:
label = ""
labels = []
if is_pinned:
label += "pinned "
if unread_count:
label += f"{unread_count} "
labels.append("pinned")
if chat["notification_settings"]["mute_for"]:
label = f"muted {label}"
labels.append("muted")
return f" {label}"
if unread_count:
labels.append(str(unread_count))
label = " ".join(labels)
if label:
return f" {label}"
return label
class MsgView:
@ -247,7 +264,8 @@ class MsgView:
# count wide character utf-8 symbols that take > 1 bytes to
# print it causes invalid offset
wide_char_len = sum(map(len, emoji_pattern.findall(msg)))
elements = tuple(map(lambda x: f" {x}", (dt, user_id, msg)))
elements = (f" {dt} ", user_id, " " + msg)
# elements = tuple(map(lambda x: f" {x}", (dt, user_id, msg)))
total_len = sum(len(e) for e in elements) + wide_char_len
needed_lines = (total_len // self.w) + 1
@ -283,7 +301,7 @@ class MsgView:
for elements, selected, line_num in msgs_to_draw:
column = 0
for attr, elem in zip(self._msg_attribures(selected), elements):
for attr, elem in zip(self._msg_attributes(selected), elements):
if not elem:
continue
self.win.addstr(line_num, column, elem, attr)
@ -291,14 +309,16 @@ class MsgView:
self._refresh()
@staticmethod
def _msg_attribures(is_selected: bool) -> Iterator[int]:
attrs = [
def _msg_attributes(self, is_selected: bool) -> Tuple[int]:
attrs = (
get_color(cyan, -1),
get_color(blue, -1),
get_color(white, -1),
]
return map(lambda x: x | reverse if is_selected else 0, attrs)
)
if is_selected:
return (attr | reverse for attr in attrs)
return attrs
def _get_user_by_id(self, user_id: int) -> str:
if user_id == 0: