diff --git a/python/doc/examples.rst b/python/doc/examples.rst index 9305fae3c..badb17725 100644 --- a/python/doc/examples.rst +++ b/python/doc/examples.rst @@ -4,52 +4,33 @@ examples ======== -Sending a Chat message from the command line ---------------------------------------------- - -Once you have :doc:`installed deltachat bindings ` -you can start playing from the python interpreter commandline. -For example you can type ``python`` and then:: - - # instantiate and configure deltachat account - import deltachat - ac = deltachat.Account("/tmp/db") - ac.set_config("addr", "address@example.org") - ac.set_config("mail_pwd", "some password") - - # start IO threads and perform configuration - ac.start() - - # create a contact and send a message - contact = ac.create_contact("someother@email.address") - chat = ac.create_chat_by_contact(contact) - chat.send_text("hi from the python interpreter command line") - - # shutdown IO threads - ac.shutdown() - - -Checkout our :doc:`api` for the various high-level things you can do -to send/receive messages, create contacts and chats. - - Receiving a Chat message from the command line ---------------------------------------------- -Instantiate an account and register a plugin to process -incoming messages: +Once you have :doc:`installed deltachat bindings ` +you can start playing from the python interpreter commandline. + +Here is a simple module that implements a bot that: + +- receives a message and sends back an "echo" message + +- terminates the bot if the message `/quit` is sent .. include:: ../examples/echo_and_quit.py :literal: -Checkout our :doc:`api` for the various high-level things you can do -to send/receive messages, create contacts and chats. +With this file in your working directory you can run the bot +by specifying a database path, an e-mail address and password of +a SMTP-IMAP account:: + python echo_and_quit.py --db /tmp/db --email ADDRESS --password PASSWORD -Looking at a real example +While this process is running you can start sending chat messages +to `ADDRESS`. + +Writing bots for real ------------------------- The `deltabot repository `_ -contains a real-life example of Python bindings usage. - +contains a little framework for writing deltachat bots in Python. diff --git a/python/doc/plugins.rst b/python/doc/plugins.rst index 1c4a5a619..f7bd45322 100644 --- a/python/doc/plugins.rst +++ b/python/doc/plugins.rst @@ -17,8 +17,10 @@ Registering a plugin -------------------- .. autofunction:: deltachat.register_global_plugin + :noindex: .. automethod:: deltachat.account.Account.add_account_plugin + :noindex: Per-Account Hook specifications diff --git a/python/examples/conftest.py b/python/examples/conftest.py new file mode 100644 index 000000000..48b66463e --- /dev/null +++ b/python/examples/conftest.py @@ -0,0 +1,2 @@ + +from deltachat.testplugin import * # noqa diff --git a/python/examples/echo_and_quit.py b/python/examples/echo_and_quit.py index d37ae2187..bc66c43e0 100644 --- a/python/examples/echo_and_quit.py +++ b/python/examples/echo_and_quit.py @@ -1,37 +1,57 @@ -# instantiate and configure deltachat account +# content of echo_and_quit.py + +import sys +import optparse import deltachat -ac = deltachat.Account("/tmp/db") -# to see low-level events in the console uncomment the following line -# ac.add_account_plugin(deltachat.eventlogger.FFIEventLogger(ac, "")) -if not ac.is_configured(): - ac.set_config("addr", "tmpy.94mtm@testrun.org") - ac.set_config("mail_pw", "5CbD6VnjD/li") - ac.set_config("mvbox_watch", "0") - ac.set_config("sentbox_watch", "0") - -class MyPlugin: +class SimpleEchoPlugin: @deltachat.hookspec.account_hookimpl def process_incoming_message(self, message): print("process_incoming message", message) if message.text.strip() == "/quit": - print("shutting down") - ac.shutdown() + message.account.shutdown() else: - ch = ac.create_chat_by_contact(message.get_sender_contact()) - ch.send_text("echoing {}".format(message.text)) + ch = message.account.create_chat_by_contact(message.get_sender_contact()) + ch.send_text("echoing from {}:\n{}".format(message.get_sender_contact().addr, message.text)) @deltachat.hookspec.account_hookimpl def process_message_delivered(self, message): print("process_message_delivered", message) -ac.add_account_plugin(MyPlugin()) -# start IO threads and perform configuration -ac.start() +def main(argv): + p = optparse.OptionParser("simple-echo") + p.add_option("-l", action="store_true", help="show low-level events") + p.add_option("--db", type="str", help="database file") + p.add_option("--email", type="str", help="email address") + p.add_option("--password", type="str", help="password") -print("waiting for /quit or other message on {}".format(ac.get_config("addr"))) + opts, posargs = p.parse_args(argv) -ac.wait_shutdown() + assert opts.db, "you must specify --db" + ac = deltachat.Account(opts.db) + + if opts.l: + ac.add_account_plugin(deltachat.eventlogger.FFIEventLogger(ac, "echo")) + + if not ac.is_configured(): + assert opts.email and opts.password, "you must specify --email and --password" + ac.set_config("addr", opts.email) + ac.set_config("mail_pw", opts.password) + ac.set_config("mvbox_watch", "0") + ac.set_config("sentbox_watch", "0") + + ac.add_account_plugin(SimpleEchoPlugin()) + + # start IO threads and perform configure if neccessary + ac.start(callback_thread=True) + + print("waiting for /quit or message to echo on: {}".format(ac.get_config("addr"))) + + ac.wait_shutdown() + + +if __name__ == "__main__": + main(sys.argv) diff --git a/python/examples/test_examples.py b/python/examples/test_examples.py new file mode 100644 index 000000000..a9f47b02b --- /dev/null +++ b/python/examples/test_examples.py @@ -0,0 +1,41 @@ + +import threading +import pytest +import py +from echo_and_quit import main + + +@pytest.fixture(scope='session') +def datadir(): + """The py.path.local object of the test-data/ directory.""" + for path in reversed(py.path.local(__file__).parts()): + datadir = path.join('test-data') + if datadir.isdir(): + return datadir + else: + pytest.skip('test-data directory not found') + + +def test_echo_quit_plugin(acfactory): + bot_ac, bot_cfg = acfactory.get_online_config() + + def run_bot(): + print("*"*20 + " starting bot") + main([ + "-l", + "--email", bot_cfg["addr"], + "--password", bot_cfg["mail_pw"], + "--db", bot_ac.db_path + ]) + + t = threading.Thread(target=run_bot) + t.start() + + ac1 = acfactory.get_one_online_account() + bot_contact = ac1.create_contact(bot_cfg["addr"]) + ch1 = ac1.create_chat_by_contact(bot_contact) + ch1.send_text("hello") + reply = ac1._evtracker.wait_next_incoming_message() + assert "hello" in reply.text + ch1.send_text("/quit") + t.join() diff --git a/python/src/deltachat/account.py b/python/src/deltachat/account.py index 4a41e0934..51349c8eb 100644 --- a/python/src/deltachat/account.py +++ b/python/src/deltachat/account.py @@ -58,6 +58,7 @@ class Account(object): db_path = db_path.encode("utf8") if not lib.dc_open(self._dc_context, db_path, ffi.NULL): raise ValueError("Could not dc_open: {}".format(db_path)) + self.db_path = db_path self._configkeys = self.get_config("sys.config_keys").split() atexit.register(self.shutdown) diff --git a/python/src/deltachat/iothreads.py b/python/src/deltachat/iothreads.py index da21ffc87..fe1d1703d 100644 --- a/python/src/deltachat/iothreads.py +++ b/python/src/deltachat/iothreads.py @@ -68,6 +68,7 @@ class IOThreads: ev = next(it) except StopIteration: break + self.account.log_line("calling hook name={} kwargs={}".format(ev.name, ev.kwargs)) ev.call_hook() def imap_thread_run(self): diff --git a/python/tox.ini b/python/tox.ini index 965607770..cfdab2957 100644 --- a/python/tox.ini +++ b/python/tox.ini @@ -8,6 +8,7 @@ envlist = [testenv] commands = pytest -n6 --reruns 2 --reruns-delay 5 -v -rsXx --ignored {posargs:tests} + pytest examples python tests/package_wheels.py {toxworkdir}/wheelhouse passenv = TRAVIS @@ -41,7 +42,7 @@ deps = restructuredtext_lint commands = flake8 src/deltachat - flake8 tests/ + flake8 tests/ examples/ rst-lint --encoding 'utf-8' README.rst [testenv:doc]