aboutsummaryrefslogtreecommitdiff
path: root/src/IRCBuffer.py
blob: 64c28e516c45d429f5dbf0661d1ec2bd22ec92e6 (about) (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
import collections, dataclasses, datetime, re, typing, uuid
from src import IRCBot, IRCServer, utils

MAX_LINES = 2**10

@dataclasses.dataclass
class BufferLine(object):
    sender: str
    message: str
    action: bool
    tags: dict
    from_self: bool
    method: str

    deleted: bool=False

    notes: typing.Dict[str, str] = dataclasses.field(
        default_factory=dict)

    id: str = dataclasses.field(
        default_factory=lambda: str(uuid.uuid4()))
    timestamp: datetime.datetime = dataclasses.field(
        default_factory=utils.datetime.utcnow)

    def format(self):
        if self.action:
            format = "* %s %s"
        else:
            format = "<%s> %s"
        return format % (self.sender, self.message)

class BufferLineMatch(object):
    def __init__(self, line: BufferLine, match: str):
        self.line = line
        self.match = match

class Buffer(object):
    def __init__(self, bot: "IRCBot.Bot", server: "IRCServer.Server"):
        self.bot = bot
        self.server = server
        self._lines: typing.Deque[BufferLine] = collections.deque(
            maxlen=MAX_LINES)

    def __len__(self) -> int:
        return len(self._lines)

    def add(self, line: BufferLine):
        self._lines.appendleft(line)

    def get(self, index: int=0, from_self=True, deleted=False
            ) -> typing.Optional[BufferLine]:
        for line in self._lines:
            if line.from_self and not from_self:
                continue
            if line.deleted and not deleted:
                continue
            return line
        return None
    def get_all(self, for_user: typing.Optional[str]=None):
        if not for_user == None:
            for line in self._lines:
                if self.server.irc_lower(line.sender) == for_user:
                    yield line
        else:
            for line in self._lines:
                yield line

    def find_all(self, pattern: typing.Union[str, typing.Pattern[str]],
            not_pattern: typing.Union[str, typing.Pattern[str]]=None,
            from_self=True, for_user: str=None, deleted=False
            ) -> typing.Generator[BufferLineMatch, None, None]:
        if for_user:
            for_user = self.server.irc_lower(for_user)

        for line in self._lines:
            if line.from_self and not from_self:
                continue
            else:
                match = re.search(pattern, line.message)
                if match:
                    if not_pattern and re.search(not_pattern, line.message):
                        continue
                    if for_user and not self.server.irc_lower(line.sender
                            ) == for_user:
                        continue
                    if line.deleted and not deleted:
                        continue
                    yield BufferLineMatch(line, match.group(0))
        return None
    def find(self, pattern: typing.Union[str, typing.Pattern[str]]
            ) -> typing.Optional[BufferLineMatch]:
        return next(self.find_all(pattern), None)

    def find_id(self, id: str) -> typing.Optional[BufferLine]:
        for line in self._lines:
            if line.id == id:
                return line
        return None

    def find_from(self, nickname: str) -> typing.Optional[BufferLine]:
        lines = self.find_many_from(nickname, 1)
        if lines:
            return lines[0]
        else:
            return None
    def find_many_from(self, nickname: str, max: int
            ) -> typing.List[BufferLine]:
        nickname_lower = self.server.irc_lower(nickname)
        found_lines = []
        for line in self._lines:
            if (not line.from_self
                    and self.server.irc_lower(line.sender) == nickname_lower):
                found_lines.append(line)
                if len(found_lines) == max:
                    break
        return found_lines