add Client.run_until()

This commit is contained in:
adbenitez
2022-12-11 03:31:29 -05:00
parent be63e18ebf
commit 2ebd3f54e6
3 changed files with 111 additions and 36 deletions

View File

@@ -1,6 +1,17 @@
"""Event loop implementations offering high level event handling/hooking.""" """Event loop implementations offering high level event handling/hooking."""
import inspect
import logging import logging
from typing import Callable, Dict, Iterable, Optional, Set, Tuple, Type, Union from typing import (
Callable,
Coroutine,
Dict,
Iterable,
Optional,
Set,
Tuple,
Type,
Union,
)
from deltachat_rpc_client.account import Account from deltachat_rpc_client.account import Account
@@ -56,6 +67,18 @@ class Client:
self.logger.debug("Account configured") self.logger.debug("Account configured")
async def run_forever(self) -> None: async def run_forever(self) -> None:
"""Process events forever."""
await self.run_until(lambda _: False)
async def run_until(
self, func: Callable[[AttrDict], Union[bool, Coroutine]]
) -> AttrDict:
"""Process events until the given callable evaluates to True.
The callable should accept an AttrDict object representing the
last processed event. The event is returned when the callable
evaluates to True.
"""
self.logger.debug("Listening to incoming events...") self.logger.debug("Listening to incoming events...")
if await self.is_configured(): if await self.is_configured():
await self.account.start_io() await self.account.start_io()
@@ -68,6 +91,12 @@ class Client:
if event.type == EventType.INCOMING_MSG: if event.type == EventType.INCOMING_MSG:
await self._process_messages() await self._process_messages()
stop = func(event)
if inspect.isawaitable(stop):
stop = await stop
if stop:
return event
async def _on_event( async def _on_event(
self, event: AttrDict, filter_type: Type[EventFilter] = RawEvent self, event: AttrDict, filter_type: Type[EventFilter] = RawEvent
) -> None: ) -> None:

View File

@@ -1,13 +1,11 @@
import json import json
import os import os
from typing import AsyncGenerator, List from typing import AsyncGenerator, List, Optional
import aiohttp import aiohttp
import pytest_asyncio import pytest_asyncio
from .account import Account from . import Account, AttrDict, Bot, Client, DeltaChat, EventType, Message
from .client import Bot
from .deltachat import DeltaChat
from .rpc import Rpc from .rpc import Rpc
@@ -51,6 +49,46 @@ class ACFactory:
await account.start_io() await account.start_io()
return accounts return accounts
async def send_message(
self,
to_account: Account,
from_account: Optional[Account] = None,
text: Optional[str] = None,
file: Optional[str] = None,
group: Optional[str] = None,
) -> Message:
if not from_account:
from_account = (await self.get_online_accounts(1))[0]
to_contact = await from_account.create_contact(
await to_account.get_config("addr")
)
if group:
to_chat = await from_account.create_group(group)
await to_chat.add_contact(to_contact)
else:
to_chat = await to_contact.create_chat()
return await to_chat.send_message(text=text, file=file)
async def process_message(
self,
to_client: Client,
from_account: Optional[Account] = None,
text: Optional[str] = None,
file: Optional[str] = None,
group: Optional[str] = None,
) -> AttrDict:
await self.send_message(
to_account=to_client.account,
from_account=from_account,
text=text,
file=file,
group=group,
)
event = await to_client.run_until(lambda e: e.type == EventType.INCOMING_MSG)
msg = await to_client.account.get_message_by_id(event.msg_id)
return await msg.get_snapshot()
@pytest_asyncio.fixture @pytest_asyncio.fixture
async def rpc(tmp_path) -> AsyncGenerator: async def rpc(tmp_path) -> AsyncGenerator:

View File

@@ -1,6 +1,8 @@
from unittest.mock import MagicMock
import pytest import pytest
from deltachat_rpc_client import AttrDict, EventType, events from deltachat_rpc_client import EventType, events
from deltachat_rpc_client.rpc import JsonRpcError from deltachat_rpc_client.rpc import JsonRpcError
@@ -216,40 +218,46 @@ async def test_message(acfactory) -> None:
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_bot(acfactory) -> None: async def test_bot(acfactory) -> None:
async def callback(e): def track(key):
res.append(e) async def wrapper(e):
mock.hook(e[key])
res = [] return wrapper
mock = MagicMock()
user = (await acfactory.get_online_accounts(1))[0]
bot = await acfactory.new_configured_bot() bot = await acfactory.new_configured_bot()
assert await bot.is_configured() assert await bot.is_configured()
assert await bot.account.get_config("bot") == "1" assert await bot.account.get_config("bot") == "1"
bot.add_hook(callback, events.RawEvent(EventType.INFO)) hook = track("msg_id"), events.RawEvent(EventType.INCOMING_MSG)
info_event = AttrDict(account=bot.account, type=EventType.INFO, msg="info") bot.add_hook(*hook)
warn_event = AttrDict(account=bot.account, type=EventType.WARNING, msg="warning") event = await acfactory.process_message(
await bot._on_event(info_event) from_account=user, to_client=bot, text="Hello!"
await bot._on_event(warn_event) )
assert info_event in res mock.hook.assert_called_once_with(event.id)
assert warn_event not in res bot.remove_hook(*hook)
assert len(res) == 1
res = [] mock.hook.reset_mock()
bot.add_hook(callback, events.NewMessage(r"hello")) hook = track("id"), events.NewMessage(r"hello")
bot.add_hook(callback, events.NewMessage(command="/help")) bot.add_hook(*hook)
snapshot1 = AttrDict(text="hello", command=None) bot.add_hook(track("id"), events.NewMessage(command="/help"))
snapshot2 = AttrDict(text="hello, world", command=None) event = await acfactory.process_message(
snapshot3 = AttrDict(text="hey!", command=None) from_account=user, to_client=bot, text="hello"
for snapshot in [snapshot1, snapshot2, snapshot3]: )
await bot._on_event(snapshot, events.NewMessage) mock.hook.assert_called_with(event.id)
assert len(res) == 2 event = await acfactory.process_message(
assert snapshot1 in res from_account=user, to_client=bot, text="hello!"
assert snapshot2 in res )
assert snapshot3 not in res mock.hook.assert_called_with(event.id)
await acfactory.process_message(from_account=user, to_client=bot, text="hey!")
assert len(mock.hook.mock_calls) == 2
bot.remove_hook(*hook)
res = [] mock.hook.reset_mock()
bot.remove_hook(callback, events.NewMessage(r"hello")) await acfactory.process_message(from_account=user, to_client=bot, text="hello")
snapshot4 = AttrDict(command="/help") event = await acfactory.process_message(
await bot._on_event(snapshot, events.NewMessage) from_account=user, to_client=bot, text="/help"
await bot._on_event(snapshot4, events.NewMessage) )
assert len(res) == 1 mock.hook.assert_called_once_with(event.id)
assert snapshot4 in res