diff --git a/functions/reacts-README.md b/functions/reacts-README.md index 947d694..9ff2374 100644 --- a/functions/reacts-README.md +++ b/functions/reacts-README.md @@ -25,6 +25,7 @@ The JSON file should have the following structure and can have the following fie "content": str | list[str], "max_limit": int (optional), "react_with_all": boolean (optional), + "react_in_order": boolean (optional) } ], "possible_replies": [ diff --git a/functions/reacts.py b/functions/reacts.py index 9d73fd9..3058e0d 100644 --- a/functions/reacts.py +++ b/functions/reacts.py @@ -7,6 +7,7 @@ from discord import Message from random import randint, sample import ast +from time import sleep from modules.probability import mock_bernoulli @@ -52,6 +53,8 @@ class ReactEvent(BaseModel): class ReactEventReaction(ReactEvent): # Whether all of the possible reactions should be added to the message. react_with_all: bool = False + # Whether to react the reactions in order + react_in_order: bool = False class ReactEventReply(ReactEvent): @@ -92,10 +95,14 @@ def load_react_resources() -> dict[str, ReactResource]: # For testing in pytest to test a specific resource -def check_resource_match(message_content: str, resource_name: str) -> bool: +def check_resource_match(message_content: str, resource_name: str, criteria_link: str | None = None) -> bool: resource = REACT_RESOURCES[resource_name] - matches = check_matches(message_content, resource.criteria) - return bool(matches) + if criteria_link: + for criteria in resource.criteria: + if criteria.criteria_link == criteria_link: + return bool(check_matches(message_content, [criteria])) + else: + return bool(check_matches(message_content, resource.criteria)) def regex_search(message_content: str, possible_match: ReactCriteria) -> bool: @@ -130,17 +137,27 @@ async def react_to_message( ) -> bool: reaction_happened = False for possible_reaction in possible_reactions: + sleep(0.005) # If the matched linked results exists check if the reaction is linked to a match if not evaluate_event_condition(possible_reaction.condition, criteria_links): continue # List of reactions in random order reactions_list = ( - sample(possible_reaction.content, len(possible_reaction.content)) - if isinstance(possible_reaction.content, list) - else [possible_reaction.content] + ( + sample(possible_reaction.content, len(possible_reaction.content)) + if isinstance(possible_reaction.content, list) + else [possible_reaction.content] + ) + if not possible_reaction.react_in_order + else ( + possible_reaction.content + if isinstance(possible_reaction.content, list) + else [possible_reaction.content] + ) ) reaction_count = 0 for reaction in reactions_list: + sleep(0.001) if possible_reaction.react_with_all: # If all of the possible reactions should be added if await add_reaction(message, reaction): reaction_happened = True @@ -169,6 +186,7 @@ async def reply_to_message( ) -> bool: reply_happened = False for possible_reply in possible_replies: + sleep(0.005) # If the matched linked results exists check if the reply is linked to a match if not evaluate_event_condition(possible_reply.condition, criteria_links): continue @@ -177,6 +195,7 @@ async def reply_to_message( if isinstance(possible_reply.content, list): reply_count = 0 for reply in possible_reply.content: + sleep(0.001) if possible_reply.max_limit > 0 and reply_count >= possible_reply.max_limit: break if await send_message( diff --git a/resources/reacts/tw.json b/resources/reacts/tw.json index 6fb1113..e22f9cf 100644 --- a/resources/reacts/tw.json +++ b/resources/reacts/tw.json @@ -54,6 +54,17 @@ "嘉義", "美麗島" ] + }, + { + "match_whole_word": true, + "keywords": [ + "Taiwan(?:[\\s-]*(?:is|be|are|=|:|\\bis\\b|\\bare\\b))?[\\s-]*(?:(?:number|#|no?\\.?|n°\\.?)\\s*(?:1|one|won|①|壹)|(?:first|1st|best|top|greatest|the\\s+best))(?![\\s-]*\\d)", + "(?:台|臺)灣(?:[\\s-]*(?:是|=|:)?)?[\\s-]*(?:(?:第|#|No\\.?)\\s*(?:1|一|壹|①)|(?:最(?:棒|強|好)|冠軍|第一名))(?![\\s-]*\\d)", + "tâi-uân[\\s-]*tē-it", + "Taiwan[\\s-]*(?:第一|最棒|最強|最好)", + "(?:台|臺)灣[\\s-]*(?:number[\\s-]*one|is[\\s-]*number[\\s-]*one)" + ], + "criteria_link": "tw_no_1" } ], "possible_reactions": [ @@ -85,6 +96,17 @@ "<:PineappleCake:1156373382565212323>", "<:StinkyTofu:1156373383643152445>" ] + }, + { + "condition": "tw_no_1", + "chance": 1, + "content": [ + "<:tw_heart:1133045893227102299>", + "#️⃣", + "1️⃣" + ], + "react_with_all": true, + "react_in_order": true } ], "possible_replies": [ diff --git a/tests/test_reacts/test_reacts_tw.py b/tests/test_reacts/test_reacts_tw.py index baadb01..3f349e7 100644 --- a/tests/test_reacts/test_reacts_tw.py +++ b/tests/test_reacts/test_reacts_tw.py @@ -57,6 +57,57 @@ "美麗島", ) +TEST_CASES_TW_NO_1_TRUE = ( + "Taiwan #1", + "Taiwan number 1", + "Taiwan is number one", + "Taiwan Number One", + "Taiwan is #1", + "Taiwan No. 1", + "Taiwan n° 1", + "臺灣第一", + "台灣最棒", + "Taiwan is the best", + "Taiwan = first", + "Taiwan: top", + "Taiwan are greatest", + "Taiwan - number 1", + "Taiwan # 1", + "臺灣第一名", + "台灣最強", + "台灣是第一", + "Taiwan 第一", + "臺灣 number one", + "TAIWAN NUMBER ONE", + "taiwan #1", + "台灣冠軍", + "臺灣 is number one", + "Taiwan第一", + "tâi-uân tē-it" +) + +TEST_CASES_TW_NO_1_FALSE = ( + "Thailand #1", + "Taiwan is great", + "Number 1 Taiwan", + "Taiwan number 2", + "Taiwan was number 1", + "Taiwanese number 1", + "Taiwan numbers", + "Taiwan", + "#1", + "Number one", + "Taiwan #", + "Taiwan number", + "Taiwan is second", + "Taiwan and Japan are both great", + "Taiwan's number 1 export", + "Taiwan has won", + "Taiwan will be number 1", + "Taiwan used to be number 1", + "Taiwan aims to be number 1" +) + ALL_TEST_CASES = TEST_CASE_EN + TEST_CASE_TW @@ -100,3 +151,35 @@ def test_react_tw_regex_no_match(test_str: str): """Tests that these strings return FALSE.""" # * surrounded by text assert not check_resource_match(f"a{test_str}b", resource_name='tw') + + +@pytest.mark.parametrize( + "test_str", + TEST_CASES_TW_NO_1_TRUE, +) +def test_react_tw_no_1_regex_yes_match(test_str: str): + """Tests that these strings return TRUE.""" + # * isolated string + assert check_resource_match(test_str, resource_name='tw', criteria_link="tw_no_1") + # * surrounded by spaces + assert check_resource_match(f" {test_str} ", resource_name='tw', criteria_link="tw_no_1") + # * to lowercase + assert check_resource_match(test_str.lower(), resource_name='tw', criteria_link="tw_no_1") + # * to title case + assert check_resource_match(test_str.title(), resource_name='tw', criteria_link="tw_no_1") + + +@pytest.mark.parametrize( + "test_str", + TEST_CASES_TW_NO_1_FALSE, +) +def test_react_tw_no_1_regex_no_match(test_str: str): + """Tests that these strings return FALSE.""" + # * isolated string + assert not check_resource_match(test_str, resource_name='tw', criteria_link="tw_no_1") + # * surrounded by spaces + assert not check_resource_match(f" {test_str} ", resource_name='tw', criteria_link="tw_no_1") + # * to lowercase + assert not check_resource_match(test_str.lower(), resource_name='tw', criteria_link="tw_no_1") + # * to title case + assert not check_resource_match(test_str.title(), resource_name='tw', criteria_link="tw_no_1")