diff --git a/services/chat-router/hive/chat_router/handlers/ping.py b/services/chat-router/hive/chat_router/handlers/ping.py new file mode 100644 index 0000000..6902a5d --- /dev/null +++ b/services/chat-router/hive/chat_router/handlers/ping.py @@ -0,0 +1,84 @@ +import logging +import re + +from typing import Optional + +from hive.messaging import Channel +from hive.chat import ChatMessage, tell_user + +from ..handler import Handler + +logger = logging.getLogger(__name__) +d = logger.info + + +class PingHandler(Handler): + def handle(self, channel: Channel, message: ChatMessage) -> bool: + challenge = message.text + if not challenge: + return False + + response = self.response_for_challenge(challenge) + if not response: + return False + + d("Challenge: %r: Response: %r", challenge, response) + tell_user(response, in_reply_to=message, channel=channel) + + return True + + STATIC_RESPONSES = { + "ping": "pong", + "pong": "ping", + "bonjour": "salop", + "salop": "bonjour", + "hello": "hi", + "hi": "hello", + } + + @classmethod + def response_for_challenge(cls, challenge: str) -> Optional[str]: + user_input = challenge.strip() + cased_challenge = cls._the_one_word(user_input) + if not cased_challenge: + return None + + lower_challenge = cased_challenge.lower() + lower_response = cls.STATIC_RESPONSES.get(lower_challenge) + if not lower_response: + return None + + extra_cased = f"{lower_challenge[0]}{cased_challenge[1:]}" + cased_challenge = list(cased_challenge) + while len(cased_challenge) < len(lower_response): + cased_challenge.extend(extra_cased) + while len(cased_challenge) > len(lower_response): + cased_challenge.pop(2) + assert len(lower_response) == len(cased_challenge) + + response = [ + b.upper() if a.isupper() else b + for a, b in zip(cased_challenge, lower_response) + ] + + if (x := sum(1 for c in user_input if c == "!")): + response.append("!" * x) # match num excls in user input + elif len(user_input) > len(lower_challenge): + response.append("!") # there was punctuation + elif not lower_challenge[1:] in user_input: + response.append("!") # uppercase after first letter + + return "".join(response) + + @staticmethod + def _the_one_word(s: str) -> Optional[str]: + words = re.finditer(r"\w+", s) + try: + first_match = next(words) + except StopIteration: + return None + try: + _ = next(words) + return None + except StopIteration: + return first_match.group(0) diff --git a/services/chat-router/tests/test_ping.py b/services/chat-router/tests/test_ping.py new file mode 100644 index 0000000..e52273f --- /dev/null +++ b/services/chat-router/tests/test_ping.py @@ -0,0 +1,28 @@ +import pytest + +from hive.chat_router.handlers.ping import PingHandler + + +@pytest.mark.parametrize( + "challenge,want_out", + (("", None), + ("He'ershingenmosiken", None), + ("ping", "pong"), + ("ping!", "pong!"), + ("PiNG", "PoNG!"), + ("pINg!", "pONg!"), + ("Bonjour", "Salop"), + ("boNJOUr", "saLOp!"), + ("boNJOUr!", "saLOp!"), + ("hello", "hi"), + ("helLO", "hi!"), + ("sALoP", "bOnJOuR!"), + ("Hi", "Hello"), + ("hi!", "hello!"), + ("Hello?", "Hi!"), + (" Hello ? ", "Hi!"), + ("HeLlo?", "Hi!"), + (" ponG ", "pinG!"), + )) +def test_responses(challenge, want_out): + assert PingHandler.response_for_challenge(challenge) == want_out diff --git a/services/matrix-router/hive/matrix_router/ping_pong.py b/services/matrix-router/hive/matrix_router/ping_pong.py deleted file mode 100644 index 2b73c63..0000000 --- a/services/matrix-router/hive/matrix_router/ping_pong.py +++ /dev/null @@ -1,60 +0,0 @@ -import re - -from typing import Optional - -from hive.messaging import Channel - - -CHALLENGE_RESPONSES = { - "ping": "pong", - "bonjour": "salop", - "hello": "hi", -} - -_WORD_RE = re.compile(r"\w+") - - -def _the_one_word(s: str) -> Optional[str]: - words = _WORD_RE.finditer(s) - try: - first_match = next(words) - except StopIteration: - return None - try: - _ = next(words) - return None - except StopIteration: - return first_match.group(0) - - -def response_for_challenge(challenge: str) -> Optional[str]: - challenge = challenge.strip() - chal = _the_one_word(challenge) - if not chal: - return None - lcha = chal.lower() - resp = CHALLENGE_RESPONSES.get(lcha) - if not resp: - return None - chal = list(chal) - while len(chal) > len(resp): - chal.pop(2) - assert len(resp) == len(chal) - result = [b.upper() if a.isupper() else b for a, b in zip(chal, resp)] - if (x := sum(1 for c in challenge if c == "!")): - result.append("!" * x) # excls in challenge - elif len(challenge) != len(lcha): - result.append("!") # punctuation - elif not lcha[1:] in challenge: - result.append("!") # uppercase - return "".join(result) - - -def route_response_for_challenge(channel: Channel, response: str): - channel.publish_request( - message={ - "format": "text", - "messages": [response] - }, - routing_key="matrix.message.send.requests", - ) diff --git a/services/matrix-router/hive/matrix_router/router.py b/services/matrix-router/hive/matrix_router/router.py index 12a8d47..6090ea7 100644 --- a/services/matrix-router/hive/matrix_router/router.py +++ b/services/matrix-router/hive/matrix_router/router.py @@ -3,7 +3,6 @@ from hive.messaging import Channel from .event import MatrixEvent -from .ping_pong import response_for_challenge, route_response_for_challenge from .reading_list import is_reading_list_update, route_reading_list_update logger = logging.getLogger(__name__) @@ -28,9 +27,6 @@ def _on_room_message(self, channel: Channel, event: MatrixEvent): raise NotImplementedError(unhandled_type) def _on_text_message(self, channel: Channel, event: MatrixEvent): - if (response := response_for_challenge(event.body)): - route_response_for_challenge(channel, response) - return if is_reading_list_update(event): route_reading_list_update(channel, event) return diff --git a/services/matrix-router/tests/test_ping_pong.py b/services/matrix-router/tests/test_ping_pong.py deleted file mode 100644 index 8026092..0000000 --- a/services/matrix-router/tests/test_ping_pong.py +++ /dev/null @@ -1,19 +0,0 @@ -import pytest - -from hive.matrix_router.ping_pong import response_for_challenge - - -@pytest.mark.parametrize( - "challenge,want_out", - (("ping", "pong"), - ("ping!", "pong!"), - ("PiNG", "PoNG!"), - ("pINg!", "pONg!"), - ("Bonjour", "Salop"), - ("boNJOUr", "saLOp!"), - ("boNJOUr!", "saLOp!"), - ("hello", "hi"), - ("helLO", "hi!"), - )) -def test_ping_pong(challenge, want_out): - assert response_for_challenge(challenge) == want_out diff --git a/services/matrix-router/tests/test_router.py b/services/matrix-router/tests/test_router.py index 4a91fe0..d5d269c 100644 --- a/services/matrix-router/tests/test_router.py +++ b/services/matrix-router/tests/test_router.py @@ -59,34 +59,3 @@ def test_reading_list_update(channel, body): }, "routing_key": "readinglist.update.requests", }] - - -@pytest.mark.parametrize( - "challenge,expect_response", - (("ping", "pong"), - ("Hello?", "Hi!"), - (" Hello ? ", "Hi!"), - ("HeLlo?", "Hi!"), - (" pinG ", "ponG!"), - )) -def test_challenge_response(channel, challenge, expect_response): - router = Router() - router.on_matrix_event(channel, MatrixEvent({ - "source": { - "type": "m.room.message", - "content": { - "msgtype": "m.text", - "body": challenge, - }, - "event_id": "$26RqwJMLw-yds1GAH_QxjHRC1Da9oasK0e5VLnck_45", - "origin_server_ts": 1730071727043, - "sender": "@neo:matrix.org", - }, - })) - assert channel.published_requests == [{ - "message": { - "format": "text", - "messages": [expect_response], - }, - "routing_key": "matrix.message.send.requests", - }]