mirror of
https://github.com/deltachat/deltachat-core.git
synced 2025-10-03 17:59:19 +02:00
improve docs and structure
see online snapshot: https://m.devpi.net/hpk/dev/deltachat/0.5.dev0/+doc/index.html
This commit is contained in:
parent
099306408e
commit
8683de19e3
11 changed files with 253 additions and 213 deletions
1
python/.gitignore
vendored
Normal file
1
python/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
doc/_build/
|
3
python/doc/_templates/globaltoc.html
vendored
3
python/doc/_templates/globaltoc.html
vendored
|
@ -4,7 +4,8 @@
|
|||
<ul>
|
||||
<li><a href="{{ pathto('index') }}">Home</a></li>
|
||||
<li><a href="{{ pathto('install') }}">Install</a></li>
|
||||
<li><a href="{{ pathto('api') }}">API Documentation</a></li>
|
||||
<li><a href="{{ pathto('api') }}">High level API</a></li>
|
||||
<li><a href="{{ pathto('capi') }}">Low level API</a></li>
|
||||
</ul>
|
||||
<b>External Links:</b>
|
||||
<ul>
|
||||
|
|
2
python/doc/_templates/sidebarintro.html
vendored
2
python/doc/_templates/sidebarintro.html
vendored
|
@ -1,4 +1,4 @@
|
|||
<h3>deltachat {{release}}</h3>
|
||||
<p>
|
||||
Bindings for <a href="https://delta.chat">Delta.chat</a>
|
||||
<a href="https://delta.chat">Delta.chat</a> Python Bindings
|
||||
</p>
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
|
||||
deltachat API Reference
|
||||
==============================
|
||||
deltachat high level API Reference
|
||||
===================================
|
||||
|
||||
.. note::
|
||||
|
||||
This API is work in progress and may change in versions prior to 1.0.
|
||||
|
||||
.. autosummary::
|
||||
|
||||
deltachat.account
|
||||
deltachat.chatting
|
||||
|
||||
account module
|
||||
--------------
|
||||
|
@ -12,3 +17,10 @@ account module
|
|||
.. automodule:: deltachat.account
|
||||
:members:
|
||||
|
||||
|
||||
chatting module
|
||||
---------------
|
||||
|
||||
.. automodule:: deltachat.chatting
|
||||
:members:
|
||||
|
||||
|
|
7
python/doc/capi.rst
Normal file
7
python/doc/capi.rst
Normal file
|
@ -0,0 +1,7 @@
|
|||
|
||||
Low Level API Reference
|
||||
===================================
|
||||
|
||||
.. automodule:: deltachat.capi.lib
|
||||
:members:
|
||||
|
|
@ -1,17 +1,15 @@
|
|||
deltachat: e-mail messaging/chatting API / deltachat-core C lib bindings
|
||||
========================================================================
|
||||
DeltaChat Python Bindings
|
||||
=========================
|
||||
|
||||
.. include:: links.rst
|
||||
The deltachat package provides two bindings for the core C-library
|
||||
of the https://delta.chat messaging ecosystem:
|
||||
|
||||
The deltachat library provides interfaces into the core
|
||||
C-library for https://delta.chat:
|
||||
- :doc:`capi` is a lowlevel CFFI-binding to the
|
||||
`deltachat-core C-API <https://deltachat.github.io/api/index.html>`_.
|
||||
|
||||
- **low level bindings to deltachat-core**: ``deltachat.capi.lib`` exposes
|
||||
a CFFI-interface to the `deltachat-core C-API <https://deltachat.github.io/api/index.html>`.
|
||||
- :doc:`api` [work-in-progress] is a high level interface to deltachat-core which aims
|
||||
to be memory safe and thoroughly tested through continous tox/pytest runs.
|
||||
|
||||
- **higher level bindings**: :class:`deltachat.Account` serves as a high
|
||||
level object through which you can configure, send and receive messages,
|
||||
create and manage groups.
|
||||
|
||||
Getting started
|
||||
-----------------------------------------
|
||||
|
@ -22,6 +20,7 @@ Getting started
|
|||
install
|
||||
getting-started
|
||||
api
|
||||
capi
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
|
|
4
python/liveconfig.txt
Normal file
4
python/liveconfig.txt
Normal file
|
@ -0,0 +1,4 @@
|
|||
addr=test1@hq5.merlinux.eu mail_pw=CzCw6oV4w7WPkTy7BHn8OmC2Vak/
|
||||
addr=test2@hq5.merlinux.eu mail_pw=J/fegsglkjJyXFncgn5MAg5t8pAq
|
||||
addr=test3@hq5.merlinux.eu mail_pw=supersimplespasswort1
|
||||
addr=test4@hq5.merlinux.eu mail_pw=supersimplespasswort2
|
|
@ -2,7 +2,6 @@ from deltachat import capi
|
|||
from deltachat.capi import ffi
|
||||
from deltachat.account import Account # noqa
|
||||
|
||||
|
||||
__version__ = "0.5.dev0"
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
""" Delta.Chat high level API objects. """
|
||||
""" Delta.Chat Account class. """
|
||||
|
||||
from __future__ import print_function
|
||||
import threading
|
||||
|
@ -12,192 +12,11 @@ except ImportError:
|
|||
|
||||
import deltachat
|
||||
from . import capi
|
||||
from .cutil import convert_to_bytes_utf8, ffi_unicode, iter_array_and_unref
|
||||
from .capi import ffi, lib
|
||||
from .types import cached_property, property_with_doc
|
||||
import attr
|
||||
from attr import validators as v
|
||||
|
||||
|
||||
@attr.s
|
||||
class EventHandler(object):
|
||||
dc_context = attr.ib(validator=v.instance_of(ffi.CData))
|
||||
|
||||
def read_url(self, url):
|
||||
try:
|
||||
r = requests.get(url)
|
||||
except requests.ConnectionError:
|
||||
return ''
|
||||
else:
|
||||
return r.content
|
||||
|
||||
def dc_event_http_get(self, data1, data2):
|
||||
url = data1
|
||||
content = self.read_url(url)
|
||||
if not isinstance(content, bytes):
|
||||
content = content.encode("utf8")
|
||||
# we need to return a fresh pointer that the core owns
|
||||
return capi.lib.dupstring_helper(content)
|
||||
|
||||
def dc_event_is_offline(self, data1, data2):
|
||||
return 0 # always online
|
||||
|
||||
|
||||
class EventLogger:
|
||||
def __init__(self, dc_context, logid=None, debug=True):
|
||||
self.dc_context = dc_context
|
||||
self._event_queue = Queue()
|
||||
self._debug = debug
|
||||
if logid is None:
|
||||
logid = str(self.dc_context).strip(">").split()[-1]
|
||||
self.logid = logid
|
||||
self._timeout = None
|
||||
|
||||
def __call__(self, evt_name, data1, data2):
|
||||
self._log_event(evt_name, data1, data2)
|
||||
self._event_queue.put((evt_name, data1, data2))
|
||||
|
||||
def set_timeout(self, timeout):
|
||||
self._timeout = timeout
|
||||
|
||||
def get(self, timeout=None, check_error=True):
|
||||
timeout = timeout or self._timeout
|
||||
ev = self._event_queue.get(timeout=timeout)
|
||||
if check_error and ev[0] == "DC_EVENT_ERROR":
|
||||
raise ValueError("{}({!r},{!r})".format(*ev))
|
||||
return ev
|
||||
|
||||
def get_matching(self, event_name_regex):
|
||||
rex = re.compile("(?:{}).*".format(event_name_regex))
|
||||
while 1:
|
||||
ev = self.get()
|
||||
if rex.match(ev[0]):
|
||||
return ev
|
||||
|
||||
def _log_event(self, evt_name, data1, data2):
|
||||
if self._debug:
|
||||
t = threading.currentThread()
|
||||
tname = getattr(t, "name", t)
|
||||
print("[{}-{}] {}({!r},{!r})".format(
|
||||
tname, self.logid, evt_name, data1, data2))
|
||||
|
||||
|
||||
@attr.s
|
||||
class Contact(object):
|
||||
""" Delta-Chat Contact. You obtain instances of it through the :class:`Account`.
|
||||
|
||||
:ivar id: integer id of this chat.
|
||||
"""
|
||||
dc_context = attr.ib(validator=v.instance_of(ffi.CData))
|
||||
id = attr.ib(validator=v.instance_of(int))
|
||||
|
||||
@cached_property # only get it once because we only free it once
|
||||
def dc_contact_t(self):
|
||||
return capi.lib.dc_get_contact(self.dc_context, self.id)
|
||||
|
||||
def __del__(self):
|
||||
if lib is not None and hasattr(self, "_property_cache"):
|
||||
lib.dc_contact_unref(self.dc_contact_t)
|
||||
|
||||
@property_with_doc
|
||||
def addr(self):
|
||||
""" normalized e-mail address for this account. """
|
||||
return ffi_unicode(capi.lib.dc_contact_get_addr(self.dc_contact_t))
|
||||
|
||||
@property_with_doc
|
||||
def display_name(self):
|
||||
""" display name for this contact. """
|
||||
return ffi_unicode(capi.lib.dc_contact_get_display_name(self.dc_contact_t))
|
||||
|
||||
def is_blocked(self):
|
||||
""" Return True if the contact is blocked. """
|
||||
return capi.lib.dc_contact_is_blocked(self.dc_contact_t)
|
||||
|
||||
def is_verified(self):
|
||||
""" Return True if the contact is verified. """
|
||||
return capi.lib.dc_contact_is_verified(self.dc_contact_t)
|
||||
|
||||
|
||||
@attr.s
|
||||
class Chat(object):
|
||||
""" Chat object which manages members and through which you can send and retrieve messages.
|
||||
"""
|
||||
|
||||
dc_context = attr.ib(validator=v.instance_of(ffi.CData))
|
||||
id = attr.ib(validator=v.instance_of(int))
|
||||
|
||||
@cached_property
|
||||
def dc_chat_t(self):
|
||||
return capi.lib.dc_get_chat(self.dc_context, self.id)
|
||||
|
||||
def __del__(self):
|
||||
if lib is not None and hasattr(self, "_property_cache"):
|
||||
lib.dc_chat_unref(self.dc_chat_t)
|
||||
|
||||
def is_deaddrop(self):
|
||||
""" return true if this chat is a deaddrop chat. """
|
||||
return self.id == lib.DC_CHAT_ID_DEADDROP
|
||||
|
||||
def send_text_message(self, msg):
|
||||
""" send a text message and return the resulting Message instance.
|
||||
|
||||
:param msg: unicode text
|
||||
:returns: the resulting :class:`Message` instance
|
||||
"""
|
||||
msg = convert_to_bytes_utf8(msg)
|
||||
print ("chat id", self.id)
|
||||
msg_id = capi.lib.dc_send_text_msg(self.dc_context, self.id, msg)
|
||||
return Message(self.dc_context, msg_id)
|
||||
|
||||
def get_messages(self):
|
||||
""" return list of messages in this chat.
|
||||
|
||||
:returns: list of :class:`Message` objects for this chat.
|
||||
"""
|
||||
dc_array_t = lib.dc_get_chat_msgs(self.dc_context, self.id, 0, 0)
|
||||
return list(iter_array_and_unref(dc_array_t, lambda x: Message(self.dc_context, x)))
|
||||
|
||||
def count_fresh_messages(self):
|
||||
""" return number of fresh messages in this chat.
|
||||
|
||||
:returns: number of fresh messages
|
||||
"""
|
||||
return lib.dc_get_fresh_msg_cnt(self.dc_context, self.id)
|
||||
|
||||
def mark_noticed(self):
|
||||
""" mark all messages in this chat as noticed.
|
||||
|
||||
Noticed messages are no longer fresh.
|
||||
"""
|
||||
return lib.dc_marknoticed_chat(self.dc_context, self.id)
|
||||
|
||||
|
||||
@attr.s
|
||||
class Message(object):
|
||||
""" Message object. """
|
||||
dc_context = attr.ib(validator=v.instance_of(ffi.CData))
|
||||
id = attr.ib(validator=v.instance_of(int))
|
||||
|
||||
@cached_property
|
||||
def dc_msg_t(self):
|
||||
return capi.lib.dc_get_msg(self.dc_context, self.id)
|
||||
|
||||
def __del__(self):
|
||||
if lib is not None and hasattr(self, "_property_cache"):
|
||||
lib.dc_msg_unref(self.dc_msg_t)
|
||||
|
||||
@property_with_doc
|
||||
def text(self):
|
||||
"""unicode representation. """
|
||||
return ffi_unicode(capi.lib.dc_msg_get_text(self.dc_msg_t))
|
||||
|
||||
@property
|
||||
def chat(self):
|
||||
"""chat this message was posted in.
|
||||
|
||||
:returns: :class:`Chat` object
|
||||
"""
|
||||
chat_id = capi.lib.dc_msg_get_chat_id(self.dc_msg_t)
|
||||
return Chat(self.dc_context, chat_id)
|
||||
from .chatting import Contact, Chat, Message
|
||||
|
||||
|
||||
class Account(object):
|
||||
|
@ -402,21 +221,64 @@ class IOThreads:
|
|||
capi.lib.dc_perform_smtp_idle(self.dc_context)
|
||||
|
||||
|
||||
def convert_to_bytes_utf8(obj):
|
||||
if obj == ffi.NULL:
|
||||
return obj
|
||||
if not isinstance(obj, bytes):
|
||||
return obj.encode("utf8")
|
||||
return obj
|
||||
@attr.s
|
||||
class EventHandler(object):
|
||||
dc_context = attr.ib(validator=v.instance_of(ffi.CData))
|
||||
|
||||
def read_url(self, url):
|
||||
try:
|
||||
r = requests.get(url)
|
||||
except requests.ConnectionError:
|
||||
return ''
|
||||
else:
|
||||
return r.content
|
||||
|
||||
def dc_event_http_get(self, data1, data2):
|
||||
url = data1
|
||||
content = self.read_url(url)
|
||||
if not isinstance(content, bytes):
|
||||
content = content.encode("utf8")
|
||||
# we need to return a fresh pointer that the core owns
|
||||
return capi.lib.dupstring_helper(content)
|
||||
|
||||
def dc_event_is_offline(self, data1, data2):
|
||||
return 0 # always online
|
||||
|
||||
|
||||
def iter_array_and_unref(dc_array_t, constructor):
|
||||
try:
|
||||
for i in range(0, lib.dc_array_get_cnt(dc_array_t)):
|
||||
yield constructor(lib.dc_array_get_id(dc_array_t, i))
|
||||
finally:
|
||||
lib.dc_array_unref(dc_array_t)
|
||||
class EventLogger:
|
||||
def __init__(self, dc_context, logid=None, debug=True):
|
||||
self.dc_context = dc_context
|
||||
self._event_queue = Queue()
|
||||
self._debug = debug
|
||||
if logid is None:
|
||||
logid = str(self.dc_context).strip(">").split()[-1]
|
||||
self.logid = logid
|
||||
self._timeout = None
|
||||
|
||||
def __call__(self, evt_name, data1, data2):
|
||||
self._log_event(evt_name, data1, data2)
|
||||
self._event_queue.put((evt_name, data1, data2))
|
||||
|
||||
def ffi_unicode(obj):
|
||||
return ffi.string(obj).decode("utf8")
|
||||
def set_timeout(self, timeout):
|
||||
self._timeout = timeout
|
||||
|
||||
def get(self, timeout=None, check_error=True):
|
||||
timeout = timeout or self._timeout
|
||||
ev = self._event_queue.get(timeout=timeout)
|
||||
if check_error and ev[0] == "DC_EVENT_ERROR":
|
||||
raise ValueError("{}({!r},{!r})".format(*ev))
|
||||
return ev
|
||||
|
||||
def get_matching(self, event_name_regex):
|
||||
rex = re.compile("(?:{}).*".format(event_name_regex))
|
||||
while 1:
|
||||
ev = self.get()
|
||||
if rex.match(ev[0]):
|
||||
return ev
|
||||
|
||||
def _log_event(self, evt_name, data1, data2):
|
||||
if self._debug:
|
||||
t = threading.currentThread()
|
||||
tname = getattr(t, "name", t)
|
||||
print("[{}-{}] {}({!r},{!r})".format(
|
||||
tname, self.logid, evt_name, data1, data2))
|
||||
|
|
133
python/src/deltachat/chatting.py
Normal file
133
python/src/deltachat/chatting.py
Normal file
|
@ -0,0 +1,133 @@
|
|||
""" Chatting related objects: Contact, Chat, Message. """
|
||||
|
||||
from . import capi
|
||||
from .cutil import convert_to_bytes_utf8, ffi_unicode, iter_array_and_unref
|
||||
from .capi import ffi, lib
|
||||
from .types import cached_property, property_with_doc
|
||||
import attr
|
||||
from attr import validators as v
|
||||
|
||||
|
||||
@attr.s
|
||||
class Contact(object):
|
||||
""" Delta-Chat Contact.
|
||||
|
||||
You obtain instances of it through :class:`deltachat.account.Account`.
|
||||
"""
|
||||
dc_context = attr.ib(validator=v.instance_of(ffi.CData))
|
||||
id = attr.ib(validator=v.instance_of(int))
|
||||
|
||||
@cached_property # only get it once because we only free it once
|
||||
def dc_contact_t(self):
|
||||
return capi.lib.dc_get_contact(self.dc_context, self.id)
|
||||
|
||||
def __del__(self):
|
||||
if lib is not None and hasattr(self, "_property_cache"):
|
||||
lib.dc_contact_unref(self.dc_contact_t)
|
||||
|
||||
@property_with_doc
|
||||
def addr(self):
|
||||
""" normalized e-mail address for this account. """
|
||||
return ffi_unicode(capi.lib.dc_contact_get_addr(self.dc_contact_t))
|
||||
|
||||
@property_with_doc
|
||||
def display_name(self):
|
||||
""" display name for this contact. """
|
||||
return ffi_unicode(capi.lib.dc_contact_get_display_name(self.dc_contact_t))
|
||||
|
||||
def is_blocked(self):
|
||||
""" Return True if the contact is blocked. """
|
||||
return capi.lib.dc_contact_is_blocked(self.dc_contact_t)
|
||||
|
||||
def is_verified(self):
|
||||
""" Return True if the contact is verified. """
|
||||
return capi.lib.dc_contact_is_verified(self.dc_contact_t)
|
||||
|
||||
|
||||
@attr.s
|
||||
class Chat(object):
|
||||
""" Chat object which manages members and through which you can send and retrieve messages.
|
||||
|
||||
You obtain instances of it through :class:`deltachat.account.Account`.
|
||||
"""
|
||||
|
||||
dc_context = attr.ib(validator=v.instance_of(ffi.CData))
|
||||
id = attr.ib(validator=v.instance_of(int))
|
||||
|
||||
@cached_property
|
||||
def dc_chat_t(self):
|
||||
return capi.lib.dc_get_chat(self.dc_context, self.id)
|
||||
|
||||
def __del__(self):
|
||||
if lib is not None and hasattr(self, "_property_cache"):
|
||||
lib.dc_chat_unref(self.dc_chat_t)
|
||||
|
||||
def is_deaddrop(self):
|
||||
""" return true if this chat is a deaddrop chat. """
|
||||
return self.id == lib.DC_CHAT_ID_DEADDROP
|
||||
|
||||
def send_text_message(self, msg):
|
||||
""" send a text message and return the resulting Message instance.
|
||||
|
||||
:param msg: unicode text
|
||||
:returns: the resulting :class:`Message` instance
|
||||
"""
|
||||
msg = convert_to_bytes_utf8(msg)
|
||||
print ("chat id", self.id)
|
||||
msg_id = capi.lib.dc_send_text_msg(self.dc_context, self.id, msg)
|
||||
return Message(self.dc_context, msg_id)
|
||||
|
||||
def get_messages(self):
|
||||
""" return list of messages in this chat.
|
||||
|
||||
:returns: list of :class:`Message` objects for this chat.
|
||||
"""
|
||||
dc_array_t = lib.dc_get_chat_msgs(self.dc_context, self.id, 0, 0)
|
||||
return list(iter_array_and_unref(dc_array_t, lambda x: Message(self.dc_context, x)))
|
||||
|
||||
def count_fresh_messages(self):
|
||||
""" return number of fresh messages in this chat.
|
||||
|
||||
:returns: number of fresh messages
|
||||
"""
|
||||
return lib.dc_get_fresh_msg_cnt(self.dc_context, self.id)
|
||||
|
||||
def mark_noticed(self):
|
||||
""" mark all messages in this chat as noticed.
|
||||
|
||||
Noticed messages are no longer fresh.
|
||||
"""
|
||||
return lib.dc_marknoticed_chat(self.dc_context, self.id)
|
||||
|
||||
|
||||
@attr.s
|
||||
class Message(object):
|
||||
""" Message object.
|
||||
|
||||
You obtain instances of it through :class:`deltachat.account.Account` or
|
||||
:class:`Chat`.
|
||||
"""
|
||||
dc_context = attr.ib(validator=v.instance_of(ffi.CData))
|
||||
id = attr.ib(validator=v.instance_of(int))
|
||||
|
||||
@cached_property
|
||||
def dc_msg_t(self):
|
||||
return capi.lib.dc_get_msg(self.dc_context, self.id)
|
||||
|
||||
def __del__(self):
|
||||
if lib is not None and hasattr(self, "_property_cache"):
|
||||
lib.dc_msg_unref(self.dc_msg_t)
|
||||
|
||||
@property_with_doc
|
||||
def text(self):
|
||||
"""unicode representation. """
|
||||
return ffi_unicode(capi.lib.dc_msg_get_text(self.dc_msg_t))
|
||||
|
||||
@property
|
||||
def chat(self):
|
||||
"""chat this message was posted in.
|
||||
|
||||
:returns: :class:`Chat` object
|
||||
"""
|
||||
chat_id = capi.lib.dc_msg_get_chat_id(self.dc_msg_t)
|
||||
return Chat(self.dc_context, chat_id)
|
22
python/src/deltachat/cutil.py
Normal file
22
python/src/deltachat/cutil.py
Normal file
|
@ -0,0 +1,22 @@
|
|||
from .capi import lib
|
||||
from .capi import ffi
|
||||
|
||||
|
||||
def convert_to_bytes_utf8(obj):
|
||||
if obj == ffi.NULL:
|
||||
return obj
|
||||
if not isinstance(obj, bytes):
|
||||
return obj.encode("utf8")
|
||||
return obj
|
||||
|
||||
|
||||
def iter_array_and_unref(dc_array_t, constructor):
|
||||
try:
|
||||
for i in range(0, lib.dc_array_get_cnt(dc_array_t)):
|
||||
yield constructor(lib.dc_array_get_id(dc_array_t, i))
|
||||
finally:
|
||||
lib.dc_array_unref(dc_array_t)
|
||||
|
||||
|
||||
def ffi_unicode(obj):
|
||||
return ffi.string(obj).decode("utf8")
|
Loading…
Add table
Add a link
Reference in a new issue