|
|
@@ -4,6 +4,7 @@ to take on them.
|
|
4
|
4
|
"""
|
|
5
|
5
|
import re
|
|
6
|
6
|
from abc import ABCMeta, abstractmethod
|
|
|
7
|
+from datetime import datetime
|
|
7
|
8
|
from typing import Any
|
|
8
|
9
|
|
|
9
|
10
|
from discord import Message, utils as discordutils
|
|
|
@@ -42,9 +43,10 @@ class PatternExpression(metaclass=ABCMeta):
|
|
42
|
43
|
pass
|
|
43
|
44
|
|
|
44
|
45
|
@abstractmethod
|
|
45
|
|
- def matches(self, message: Message) -> bool:
|
|
|
46
|
+ def matches(self, message: Message, other_fields: dict[str: Any]) -> bool:
|
|
46
|
47
|
"""
|
|
47
|
|
- Whether a message matches this expression.
|
|
|
48
|
+ Whether a message matches this expression. other_fields are additional
|
|
|
49
|
+ fields that can be queried not contained in the message itself.
|
|
48
|
50
|
"""
|
|
49
|
51
|
return False
|
|
50
|
52
|
|
|
|
@@ -59,7 +61,7 @@ class PatternSimpleExpression(PatternExpression):
|
|
59
|
61
|
self.operator = operator
|
|
60
|
62
|
self.value = value
|
|
61
|
63
|
|
|
62
|
|
- def __field_value(self, message: Message) -> Any:
|
|
|
64
|
+ def __field_value(self, message: Message, other_fields: dict[str: Any]) -> Any:
|
|
63
|
65
|
if self.field in ('content.markdown', 'content'):
|
|
64
|
66
|
return message.content
|
|
65
|
67
|
if self.field == 'content.plain':
|
|
|
@@ -72,11 +74,14 @@ class PatternSimpleExpression(PatternExpression):
|
|
72
|
74
|
return message.created_at - message.author.joined_at
|
|
73
|
75
|
if self.field == 'author.name':
|
|
74
|
76
|
return message.author.name
|
|
75
|
|
- else:
|
|
76
|
|
- raise ValueError(f'Bad field name {self.field}')
|
|
77
|
|
-
|
|
78
|
|
- def matches(self, message: Message) -> bool:
|
|
79
|
|
- field_value = self.__field_value(message)
|
|
|
77
|
+ if self.field == 'lastmatched':
|
|
|
78
|
+ long_ago = datetime(year=1900, month=1, day=1, hour=0, minute=0, second=0)
|
|
|
79
|
+ last_matched = other_fields.get('last_matched') or long_ago
|
|
|
80
|
+ return message.created_at - last_matched
|
|
|
81
|
+ raise ValueError(f'Bad field name {self.field}')
|
|
|
82
|
+
|
|
|
83
|
+ def matches(self, message: Message, other_fields: dict[str, Any]) -> bool:
|
|
|
84
|
+ field_value = self.__field_value(message, other_fields)
|
|
80
|
85
|
if self.operator == '==':
|
|
81
|
86
|
if isinstance(field_value, str) and isinstance(self.value, str):
|
|
82
|
87
|
return field_value.lower() == self.value.lower()
|
|
|
@@ -116,17 +121,17 @@ class PatternCompoundExpression(PatternExpression):
|
|
116
|
121
|
self.operator = operator
|
|
117
|
122
|
self.operands = list(operands)
|
|
118
|
123
|
|
|
119
|
|
- def matches(self, message: Message) -> bool:
|
|
|
124
|
+ def matches(self, message: Message, other_fields: dict[str, Any]) -> bool:
|
|
120
|
125
|
if self.operator == '!':
|
|
121
|
|
- return not self.operands[0].matches(message)
|
|
|
126
|
+ return not self.operands[0].matches(message, other_fields)
|
|
122
|
127
|
if self.operator == 'and':
|
|
123
|
128
|
for op in self.operands:
|
|
124
|
|
- if not op.matches(message):
|
|
|
129
|
+ if not op.matches(message, other_fields):
|
|
125
|
130
|
return False
|
|
126
|
131
|
return True
|
|
127
|
132
|
if self.operator == 'or':
|
|
128
|
133
|
for op in self.operands:
|
|
129
|
|
- if op.matches(message):
|
|
|
134
|
+ if op.matches(message, other_fields):
|
|
130
|
135
|
return True
|
|
131
|
136
|
return False
|
|
132
|
137
|
raise ValueError(f'Bad operator "{self.operator}"')
|
|
|
@@ -208,14 +213,14 @@ class PatternCompiler:
|
|
208
|
213
|
TYPE_TIMESPAN = 'timespan'
|
|
209
|
214
|
|
|
210
|
215
|
FIELD_TO_TYPE: dict[str, str] = {
|
|
211
|
|
- 'content.plain': TYPE_TEXT,
|
|
212
|
|
- 'content.markdown': TYPE_TEXT,
|
|
213
|
216
|
'author': TYPE_MEMBER,
|
|
214
|
217
|
'author.id': TYPE_ID,
|
|
215
|
|
- 'author.name': TYPE_TEXT,
|
|
216
|
218
|
'author.joinage': TYPE_TIMESPAN,
|
|
217
|
|
-
|
|
|
219
|
+ 'author.name': TYPE_TEXT,
|
|
218
|
220
|
'content': TYPE_TEXT, # deprecated, use content.markdown or content.plain
|
|
|
221
|
+ 'content.markdown': TYPE_TEXT,
|
|
|
222
|
+ 'content.plain': TYPE_TEXT,
|
|
|
223
|
+ 'lastmatched': TYPE_TIMESPAN,
|
|
219
|
224
|
}
|
|
220
|
225
|
DEPRECATED_FIELDS: set[str] = set([ 'content' ])
|
|
221
|
226
|
|