def open_url(self) -> None: msg = MsgProxy(self.model.current_msg) if not msg.is_text: return self.present_error("Does not contain urls") text = msg["content"]["text"]["text"] urls = [] for entity in msg["content"]["text"]["entities"]: _type = entity["type"]["@type"] if _type == "textEntityTypeUrl": offset = entity["offset"] length = entity["length"] url = text[offset:offset + length] elif _type == "textEntityTypeTextUrl": url = entity["type"]["url"] else: continue urls.append(url) if not urls: return self.present_error("No url to open") if len(urls) == 1: with suspend(self.view) as s: s.call( config.DEFAULT_OPEN.format(file_path=shlex.quote(urls[0]))) return with suspend(self.view) as s: s.run_with_input(config.URL_VIEW, "\n".join(urls))
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 = { "animation": self.tg.send_animation, "image": self.tg.send_photo, "audio": self.tg.send_audio, "video": self._send_video, } mime = get_mime(file_path) if mime in ("image", "video", "animation"): resp = self.view.status.get_input( 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 = "" fun = mime_map.get(mime, self.tg.send_doc) fun(file_path, chat_id)
def record_voice(self): file_path = f"/tmp/voice-{datetime.now()}.oga" with suspend(self.view) as s: s.call( config.VOICE_RECORD_CMD.format( file_path=shlex.quote(file_path) ) ) 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 not os.path.isfile(file_path): self.present_info(f"Can't load recording file {file_path}") return chat_id = self.model.chats.id_by_index(self.model.current_chat) if not chat_id: return duration = get_duration(file_path) waveform = get_waveform(file_path) self.tg.send_voice(file_path, chat_id, duration, waveform) self.present_info(f"Sent voice msg: {file_path}")
def show_chat_info(self) -> None: """Show chat information""" chat = self.model.chats.chats[self.model.current_chat] info = self.model.get_chat_info(chat) with suspend(self.view) as s: s.run_with_input( config.VIEW_TEXT_CMD, "\n".join(f"{k}: {v}" for k, v in info.items() if v), )
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, cmd) return path = msg.local_path if not path: self.present_info("File should be downloaded first") return chat_id = self.model.chats.id_by_index(self.model.current_chat) if not chat_id: return self.tg.open_message_content(chat_id, msg.msg_id) with suspend(self.view) as s: s.open_file(path, cmd)
def show_user_info(self) -> None: """Show user profile""" msg = MsgProxy(self.model.current_msg) user_id = msg.sender_id info = self.model.get_user_info(user_id) with suspend(self.view) as s: s.run_with_input( config.VIEW_TEXT_CMD, "\n".join(f"{k}: {v}" for k, v in info.items() if v), )
def write_long_msg(self): if not self.can_send_msg(): self.present_info("Can't send msg in this chat") return with NamedTemporaryFile("r+", suffix=".txt") as f, suspend( self.view ) as s: s.call(config.LONG_MSG_CMD.format(file_path=shlex.quote(f.name))) with open(f.name) as f: if msg := f.read().strip(): self.model.send_message(text=msg) self.present_info("Message sent")
def write_long_msg(self) -> None: chat_id = self.model.chats.id_by_index(self.model.current_chat) if not self.can_send_msg() or chat_id is None: self.present_info("Can't send msg in this chat") return with NamedTemporaryFile("r+", suffix=".txt") as f, suspend(self.view) as s: self.tg.send_chat_action(chat_id, ChatAction.chatActionTyping) s.call(config.LONG_MSG_CMD.format(file_path=shlex.quote(f.name))) with open(f.name) as f: if msg := f.read().strip(): self.model.send_message(text=msg) self.present_info("Message sent") else:
def _get_user_ids(self, is_multiple: bool = False) -> List[int]: users = self.model.users.get_users() _, cols = self.view.stdscr.getmaxyx() limit = min( int(cols / 2), max(len(user.name) for user in users), ) users_out = "\n".join( f"{user.id}\t{user.name:<{limit}} | {user.status}" for user in sorted(users, key=lambda user: user.order)) cmd = config.FZF + " -n 2" if is_multiple: cmd += " -m" with NamedTemporaryFile("r+") as tmp, suspend(self.view) as s: s.run_with_input(f"{cmd} > {tmp.name}", users_out) with open(tmp.name) as f: return [int(line.split()[0]) for line in f.readlines()]
def reply_with_long_message(self): if not self.can_send_msg(): self.present_info("Can't send msg in this chat") return chat_id = self.model.current_chat_id reply_to_msg = self.model.current_msg_id msg = MsgProxy(self.model.current_msg) with NamedTemporaryFile("w+", suffix=".txt") as f, suspend( self.view ) as s: f.write(insert_replied_msg(msg)) f.seek(0) s.call(config.LONG_MSG_CMD.format(file_path=shlex.quote(f.name))) with open(f.name) as f: if msg := strip_replied_msg(f.read().strip()): self.tg.reply_message(chat_id, reply_to_msg, msg) self.present_info("Message sent") else:
def edit_msg(self) -> None: msg = MsgProxy(self.model.current_msg) log.info("Editing msg: %s", msg.msg) if not self.model.is_me(msg.sender_id): return self.present_error("You can edit only your messages!") if not msg.is_text: return self.present_error("You can edit text messages only!") if not msg.can_be_edited: return self.present_error("Meessage can't be edited!") with NamedTemporaryFile("r+", suffix=".txt") as f, suspend(self.view) as s: f.write(msg.text_content) f.flush() s.call(f"{config.EDITOR} {f.name}") with open(f.name) as f: if text := f.read().strip(): self.model.edit_message(text=text) self.present_info("Message edited")
def view_contacts(self) -> None: contacts = self.model.users.get_contacts() if contacts is None: return self.present_error("Can't get contacts") total = contacts["total_count"] users = [] for user_id in contacts["user_ids"]: user_name = get_user_label(self.model.users, user_id) status = self.model.users.get_status(user_id) order = self.model.users.get_user_status_order(user_id) users.append((user_name, status, order)) _, cols = self.view.stdscr.getmaxyx() limit = min(int(cols / 2), max(len(user_name) for user_name, *_ in users)) users_out = "\n".join( f"{user_name:<{limit}} | {status}" for user_name, status, _ in sorted(users, key=lambda it: it[2])) with suspend(self.view) as s: s.run_with_input(config.VIEW_TEXT_CMD, f"{total} users:\n" + users_out)
def breakpoint(self) -> None: with suspend(self.view): breakpoint()
def show_msg_help(self) -> None: _help = self.format_help(msg_handler) with suspend(self.view) as s: s.run_with_input(config.VIEW_TEXT_CMD, _help)
def breakpoint(self): with suspend(self.view): breakpoint()
def show_msg_help(self): _help = self.format_help(msg_handler) with suspend(self.view) as s: s.run_with_input(config.HELP_CMD, _help)