apply isort and black formatters, add format checking to CI

This commit is contained in:
adbenitez
2022-05-29 21:11:49 -04:00
parent 62b50c87d4
commit 16e0f0e986
26 changed files with 899 additions and 575 deletions

View File

@@ -2,20 +2,21 @@
import os
import re
from . import props
from .cutil import from_dc_charpointer, from_optional_dc_charpointer, as_dc_charpointer
from .capi import lib, ffi
from . import const
from datetime import datetime, timezone
from typing import Optional
from . import const, props
from .capi import ffi, lib
from .cutil import as_dc_charpointer, from_dc_charpointer, from_optional_dc_charpointer
class Message(object):
""" Message object.
"""Message object.
You obtain instances of it through :class:`deltachat.account.Account` or
:class:`deltachat.chat.Chat`.
"""
def __init__(self, account, dc_msg):
self.account = account
assert isinstance(self.account._dc_context, ffi.CData)
@@ -32,20 +33,26 @@ class Message(object):
c = self.get_sender_contact()
typ = "outgoing" if self.is_outgoing() else "incoming"
return "<Message {} sys={} {} id={} sender={}/{} chat={}/{}>".format(
typ, self.is_system_message(), repr(self.text[:10]),
self.id, c.id, c.addr, self.chat.id, self.chat.get_name())
typ,
self.is_system_message(),
repr(self.text[:10]),
self.id,
c.id,
c.addr,
self.chat.id,
self.chat.get_name(),
)
@classmethod
def from_db(cls, account, id):
assert id > 0
return cls(account, ffi.gc(
lib.dc_get_msg(account._dc_context, id),
lib.dc_msg_unref
))
return cls(
account, ffi.gc(lib.dc_get_msg(account._dc_context, id), lib.dc_msg_unref)
)
@classmethod
def new_empty(cls, account, view_type):
""" create a non-persistent message.
"""create a non-persistent message.
:param: view_type is the message type code or one of the strings:
"text", "audio", "video", "file", "sticker"
@@ -54,13 +61,15 @@ class Message(object):
view_type_code = view_type
else:
view_type_code = get_viewtype_code_from_name(view_type)
return Message(account, ffi.gc(
lib.dc_msg_new(account._dc_context, view_type_code),
lib.dc_msg_unref
))
return Message(
account,
ffi.gc(
lib.dc_msg_new(account._dc_context, view_type_code), lib.dc_msg_unref
),
)
def create_chat(self):
""" create or get an existing chat (group) object for this message.
"""create or get an existing chat (group) object for this message.
If the message is a contact request
the sender will become an accepted contact.
@@ -72,23 +81,27 @@ class Message(object):
@props.with_doc
def id(self):
"""id of this message. """
"""id of this message."""
return lib.dc_msg_get_id(self._dc_msg)
@props.with_doc
def text(self) -> str:
"""unicode text of this messages (might be empty if not a text message). """
"""unicode text of this messages (might be empty if not a text message)."""
return from_dc_charpointer(lib.dc_msg_get_text(self._dc_msg))
def set_text(self, text):
"""set text of this message. """
"""set text of this message."""
lib.dc_msg_set_text(self._dc_msg, as_dc_charpointer(text))
@props.with_doc
def html(self) -> str:
"""html text of this messages (might be empty if not an html message). """
return from_optional_dc_charpointer(
lib.dc_get_msg_html(self.account._dc_context, self.id)) or ""
"""html text of this messages (might be empty if not an html message)."""
return (
from_optional_dc_charpointer(
lib.dc_get_msg_html(self.account._dc_context, self.id)
)
or ""
)
def has_html(self):
"""return True if this message has an html part, False otherwise."""
@@ -103,11 +116,11 @@ class Message(object):
@props.with_doc
def filename(self):
"""filename if there was an attachment, otherwise empty string. """
"""filename if there was an attachment, otherwise empty string."""
return from_dc_charpointer(lib.dc_msg_get_file(self._dc_msg))
def set_file(self, path, mime_type=None):
"""set file for this message from path and mime_type. """
"""set file for this message from path and mime_type."""
mtype = ffi.NULL if mime_type is None else as_dc_charpointer(mime_type)
if not os.path.exists(path):
raise ValueError("path does not exist: {!r}".format(path))
@@ -115,7 +128,7 @@ class Message(object):
@props.with_doc
def basename(self) -> str:
"""basename of the attachment if it exists, otherwise empty string. """
"""basename of the attachment if it exists, otherwise empty string."""
# FIXME, it does not return basename
return from_dc_charpointer(lib.dc_msg_get_filename(self._dc_msg))
@@ -125,42 +138,42 @@ class Message(object):
return from_dc_charpointer(lib.dc_msg_get_filemime(self._dc_msg))
def is_system_message(self):
""" return True if this message is a system/info message. """
"""return True if this message is a system/info message."""
return bool(lib.dc_msg_is_info(self._dc_msg))
def is_setup_message(self):
""" return True if this message is a setup message. """
"""return True if this message is a setup message."""
return lib.dc_msg_is_setupmessage(self._dc_msg)
def get_setupcodebegin(self) -> str:
""" return the first characters of a setup code in a setup message. """
"""return the first characters of a setup code in a setup message."""
return from_dc_charpointer(lib.dc_msg_get_setupcodebegin(self._dc_msg))
def is_encrypted(self):
""" return True if this message was encrypted. """
"""return True if this message was encrypted."""
return bool(lib.dc_msg_get_showpadlock(self._dc_msg))
def is_bot(self):
""" return True if this message is submitted automatically. """
"""return True if this message is submitted automatically."""
return bool(lib.dc_msg_is_bot(self._dc_msg))
def is_forwarded(self):
""" return True if this message was forwarded. """
"""return True if this message was forwarded."""
return bool(lib.dc_msg_is_forwarded(self._dc_msg))
def get_message_info(self) -> str:
""" Return informational text for a single message.
"""Return informational text for a single message.
The text is multiline and may contain eg. the raw text of the message.
"""
return from_dc_charpointer(lib.dc_get_msg_info(self.account._dc_context, self.id))
return from_dc_charpointer(
lib.dc_get_msg_info(self.account._dc_context, self.id)
)
def continue_key_transfer(self, setup_code):
""" extract key and use it as primary key for this account. """
"""extract key and use it as primary key for this account."""
res = lib.dc_continue_key_transfer(
self.account._dc_context,
self.id,
as_dc_charpointer(setup_code)
self.account._dc_context, self.id, as_dc_charpointer(setup_code)
)
if res == 0:
raise ValueError("could not decrypt")
@@ -230,7 +243,7 @@ class Message(object):
lib.dc_msg_force_plaintext(self._dc_msg)
def get_mime_headers(self):
""" return mime-header object for an incoming message.
"""return mime-header object for an incoming message.
This only returns a non-None object if ``save_mime_headers``
config option was set and the message is incoming.
@@ -238,6 +251,7 @@ class Message(object):
:returns: email-mime message object (with headers only, no body).
"""
import email.parser
mime_headers = lib.dc_get_mime_headers(self.account._dc_context, self.id)
if mime_headers:
s = ffi.string(ffi.gc(mime_headers, lib.dc_str_unref))
@@ -257,6 +271,7 @@ class Message(object):
:returns: :class:`deltachat.chat.Chat` object
"""
from .chat import Chat
chat_id = lib.dc_msg_get_chat_id(self._dc_msg)
return Chat(self.account, chat_id)
@@ -267,12 +282,12 @@ class Message(object):
Usually used to impersonate someone else.
"""
return from_optional_dc_charpointer(
lib.dc_msg_get_override_sender_name(self._dc_msg))
lib.dc_msg_get_override_sender_name(self._dc_msg)
)
def set_override_sender_name(self, name):
"""set different sender name for a message. """
lib.dc_msg_set_override_sender_name(
self._dc_msg, as_dc_charpointer(name))
"""set different sender name for a message."""
lib.dc_msg_set_override_sender_name(self._dc_msg, as_dc_charpointer(name))
def get_sender_chat(self):
"""return the 1:1 chat with the sender of this message.
@@ -287,6 +302,7 @@ class Message(object):
:returns: :class:`deltachat.chat.Contact` instance
"""
from .contact import Contact
contact_id = lib.dc_msg_get_from_id(self._dc_msg)
return Contact(self.account, contact_id)
@@ -300,13 +316,12 @@ class Message(object):
else:
# load message from db to get a fresh/current state
dc_msg = ffi.gc(
lib.dc_get_msg(self.account._dc_context, self.id),
lib.dc_msg_unref
lib.dc_get_msg(self.account._dc_context, self.id), lib.dc_msg_unref
)
return lib.dc_msg_get_state(dc_msg)
def is_in_fresh(self):
""" return True if Message is incoming fresh message (un-noticed).
"""return True if Message is incoming fresh message (un-noticed).
Fresh messages are not noticed nor seen and are typically
shown in notifications.
@@ -330,25 +345,25 @@ class Message(object):
return self._msgstate == const.DC_STATE_IN_SEEN
def is_outgoing(self):
"""Return True if Message is outgoing. """
"""Return True if Message is outgoing."""
return self._msgstate in (
const.DC_STATE_OUT_PREPARING, const.DC_STATE_OUT_PENDING,
const.DC_STATE_OUT_FAILED, const.DC_STATE_OUT_MDN_RCVD,
const.DC_STATE_OUT_DELIVERED)
const.DC_STATE_OUT_PREPARING,
const.DC_STATE_OUT_PENDING,
const.DC_STATE_OUT_FAILED,
const.DC_STATE_OUT_MDN_RCVD,
const.DC_STATE_OUT_DELIVERED,
)
def is_out_preparing(self):
"""Return True if Message is outgoing, but its file is being prepared.
"""
"""Return True if Message is outgoing, but its file is being prepared."""
return self._msgstate == const.DC_STATE_OUT_PREPARING
def is_out_pending(self):
"""Return True if Message is outgoing, but is pending (no single checkmark).
"""
"""Return True if Message is outgoing, but is pending (no single checkmark)."""
return self._msgstate == const.DC_STATE_OUT_PENDING
def is_out_failed(self):
"""Return True if Message is unrecoverably failed.
"""
"""Return True if Message is unrecoverably failed."""
return self._msgstate == const.DC_STATE_OUT_FAILED
def is_out_delivered(self):
@@ -375,48 +390,48 @@ class Message(object):
return lib.dc_msg_get_viewtype(self._dc_msg)
def is_text(self):
""" return True if it's a text message. """
"""return True if it's a text message."""
return self._view_type == const.DC_MSG_TEXT
def is_image(self):
""" return True if it's an image message. """
"""return True if it's an image message."""
return self._view_type == const.DC_MSG_IMAGE
def is_gif(self):
""" return True if it's a gif message. """
"""return True if it's a gif message."""
return self._view_type == const.DC_MSG_GIF
def is_sticker(self):
""" return True if it's a sticker message. """
"""return True if it's a sticker message."""
return self._view_type == const.DC_MSG_STICKER
def is_audio(self):
""" return True if it's an audio message. """
"""return True if it's an audio message."""
return self._view_type == const.DC_MSG_AUDIO
def is_video(self):
""" return True if it's a video message. """
"""return True if it's a video message."""
return self._view_type == const.DC_MSG_VIDEO
def is_file(self):
""" return True if it's a file message. """
"""return True if it's a file message."""
return self._view_type == const.DC_MSG_FILE
def mark_seen(self):
""" mark this message as seen. """
"""mark this message as seen."""
self.account.mark_seen_messages([self.id])
# some code for handling DC_MSG_* view types
_view_type_mapping = {
'text': const.DC_MSG_TEXT,
'image': const.DC_MSG_IMAGE,
'gif': const.DC_MSG_GIF,
'audio': const.DC_MSG_AUDIO,
'video': const.DC_MSG_VIDEO,
'file': const.DC_MSG_FILE,
'sticker': const.DC_MSG_STICKER,
"text": const.DC_MSG_TEXT,
"image": const.DC_MSG_IMAGE,
"gif": const.DC_MSG_GIF,
"audio": const.DC_MSG_AUDIO,
"video": const.DC_MSG_VIDEO,
"file": const.DC_MSG_FILE,
"sticker": const.DC_MSG_STICKER,
}
@@ -424,14 +439,17 @@ def get_viewtype_code_from_name(view_type_name):
code = _view_type_mapping.get(view_type_name)
if code is not None:
return code
raise ValueError("message typecode not found for {!r}, "
"available {!r}".format(view_type_name, list(_view_type_mapping.keys())))
raise ValueError(
"message typecode not found for {!r}, "
"available {!r}".format(view_type_name, list(_view_type_mapping.keys()))
)
#
# some helper code for turning system messages into hook events
#
def map_system_message(msg):
if msg.is_system_message():
res = parse_system_add_remove(msg.text)
@@ -448,7 +466,7 @@ def map_system_message(msg):
def extract_addr(text):
m = re.match(r'.*\((.+@.+)\)', text)
m = re.match(r".*\((.+@.+)\)", text)
if m:
text = m.group(1)
text = text.rstrip(".")
@@ -456,9 +474,9 @@ def extract_addr(text):
def parse_system_add_remove(text):
""" return add/remove info from parsing the given system message text.
"""return add/remove info from parsing the given system message text.
returns a (action, affected, actor) triple """
returns a (action, affected, actor) triple"""
# Member Me (x@y) removed by a@b.
# Member x@y added by a@b
@@ -467,7 +485,7 @@ def parse_system_add_remove(text):
# Group left by some one (tmp1@x.org).
# Group left by tmp1@x.org.
text = text.lower()
m = re.match(r'member (.+) (removed|added) by (.+)', text)
m = re.match(r"member (.+) (removed|added) by (.+)", text)
if m:
affected, action, actor = m.groups()
return action, extract_addr(affected), extract_addr(actor)