11# pytype: skip-file
22import inspect
3+ import re
34import sys
45
56from slack_bolt .error import BoltError
@@ -95,37 +96,10 @@ def func(body: Dict[str, Any]) -> bool:
9596
9697 def func (body : Dict [str , Any ]) -> bool :
9798 if is_event (body ):
98- event = body ["event" ]
99- if not _matches (constraints ["type" ], event ["type" ]):
100- return False
101- if "subtype" in constraints :
102- expected_subtype : Union [
103- str , Sequence [Optional [Union [str , Pattern ]]]
104- ] = constraints ["subtype" ]
105- if expected_subtype is None :
106- # "subtype" in constraints is intentionally None for this pattern
107- return "subtype" not in event
108- elif isinstance (expected_subtype , (str , Pattern )):
109- return "subtype" in event and _matches (
110- expected_subtype , event ["subtype" ]
111- )
112- elif isinstance (expected_subtype , Sequence ):
113- subtypes : Sequence [
114- Optional [Union [str , Pattern ]]
115- ] = expected_subtype
116- for expected in subtypes :
117- actual : Optional [str ] = event .get ("subtype" )
118- if expected is None :
119- if actual is None :
120- return True
121- elif actual is not None and _matches (expected , actual ):
122- return True
123- return False
124- else :
125- return "subtype" in event and _matches (
126- expected_subtype , event ["subtype" ]
127- )
128- return True
99+ return _check_event_subtype (
100+ event_payload = body ["event" ],
101+ constraints = constraints ,
102+ )
129103 return False
130104
131105 return build_listener_matcher (func , asyncio )
@@ -135,6 +109,64 @@ def func(body: Dict[str, Any]) -> bool:
135109 )
136110
137111
112+ def message_event (
113+ constraints : Dict [str , Union [str , Sequence [Optional [Union [str , Pattern ]]]]],
114+ keyword : Union [str , Pattern ],
115+ asyncio : bool = False ,
116+ ) -> Union [ListenerMatcher , "AsyncListenerMatcher" ]:
117+ if "type" in constraints and keyword is not None :
118+ _verify_message_event_type (constraints ["type" ])
119+
120+ def func (body : Dict [str , Any ]) -> bool :
121+ if is_event (body ):
122+ is_valid_subtype = _check_event_subtype (
123+ event_payload = body ["event" ],
124+ constraints = constraints ,
125+ )
126+ if is_valid_subtype is True :
127+ # Check keyword matching
128+ text = body .get ("event" , {}).get ("text" , "" )
129+ match_result = re .findall (keyword , text )
130+ if match_result is not None and match_result != []:
131+ return True
132+ return False
133+
134+ return build_listener_matcher (func , asyncio )
135+
136+ raise BoltError (f"event ({ constraints } : { type (constraints )} ) must be dict" )
137+
138+
139+ def _check_event_subtype (event_payload : dict , constraints : dict ) -> bool :
140+ if not _matches (constraints ["type" ], event_payload ["type" ]):
141+ return False
142+ if "subtype" in constraints :
143+ expected_subtype : Union [
144+ str , Sequence [Optional [Union [str , Pattern ]]]
145+ ] = constraints ["subtype" ]
146+ if expected_subtype is None :
147+ # "subtype" in constraints is intentionally None for this pattern
148+ return "subtype" not in event_payload
149+ elif isinstance (expected_subtype , (str , Pattern )):
150+ return "subtype" in event_payload and _matches (
151+ expected_subtype , event_payload ["subtype" ]
152+ )
153+ elif isinstance (expected_subtype , Sequence ):
154+ subtypes : Sequence [Optional [Union [str , Pattern ]]] = expected_subtype
155+ for expected in subtypes :
156+ actual : Optional [str ] = event_payload .get ("subtype" )
157+ if expected is None :
158+ if actual is None :
159+ return True
160+ elif actual is not None and _matches (expected , actual ):
161+ return True
162+ return False
163+ else :
164+ return "subtype" in event_payload and _matches (
165+ expected_subtype , event_payload ["subtype" ]
166+ )
167+ return True
168+
169+
138170def _verify_message_event_type (event_type : str ) -> None :
139171 if isinstance (event_type , str ) and event_type .startswith ("message." ):
140172 raise ValueError (error_message_event_type (event_type ))
0 commit comments