Philipp Hörist pushed to branch master at gajim / gajim

Commits:

14 changed files:

Changes:

  • gajim/common/app.py
    ... ... @@ -59,6 +59,7 @@
    59 59
         from gajim.gui.application import GajimApplication
    
    60 60
         from gajim.common.storage.cache import CacheStorage
    
    61 61
         from gajim.common.storage.archive import MessageArchiveStorage
    
    62
    +    from gajim.common.storage.events import EventStorage
    
    62 63
         from gajim.common.cert_store import CertificateStore
    
    63 64
         from gajim.common.call_manager import CallManager
    
    64 65
         from gajim.common.preview import PreviewManager
    
    ... ... @@ -88,6 +89,7 @@ class Storage:
    88 89
         def __init__(self):
    
    89 90
             self.cache: CacheStorage = None
    
    90 91
             self.archive: MessageArchiveStorage = None
    
    92
    +        self.events: EventStorage = None
    
    91 93
     
    
    92 94
     
    
    93 95
     storage = Storage()
    

  • gajim/common/application.py
    ... ... @@ -44,6 +44,7 @@
    44 44
     from gajim.common.events import GajimUpdateAvailable
    
    45 45
     from gajim.common.client import Client
    
    46 46
     from gajim.common.helpers import make_http_request
    
    47
    +from gajim.common.storage.events import EventStorage
    
    47 48
     from gajim.common.task_manager import TaskManager
    
    48 49
     from gajim.common.settings import Settings
    
    49 50
     from gajim.common.settings import LegacyConfig
    
    ... ... @@ -74,6 +75,9 @@ def _init_core(self) -> None:
    74 75
             app.storage.cache = CacheStorage()
    
    75 76
             app.storage.cache.init()
    
    76 77
     
    
    78
    +        app.storage.events = EventStorage()
    
    79
    +        app.storage.events.init()
    
    80
    +
    
    77 81
             app.storage.archive = MessageArchiveStorage()
    
    78 82
             app.storage.archive.init()
    
    79 83
     
    

  • gajim/common/events.py
    ... ... @@ -32,7 +32,10 @@
    32 32
     from nbxmpp.structs import LocationData
    
    33 33
     from nbxmpp.structs import RosterItem
    
    34 34
     from nbxmpp.structs import TuneData
    
    35
    +from nbxmpp.const import Affiliation
    
    35 36
     from nbxmpp.const import InviteType
    
    37
    +from nbxmpp.const import Role
    
    38
    +from nbxmpp.const import StatusCode
    
    36 39
     
    
    37 40
     from gajim.common.file_props import FileProp
    
    38 41
     from gajim.common.const import JingleState
    
    ... ... @@ -676,3 +679,101 @@ class AllowGajimUpdateCheck(ApplicationEvent):
    676 679
     class GajimUpdateAvailable(ApplicationEvent):
    
    677 680
         name: str = field(init=False, default='gajim-update-available')
    
    678 681
         version: str
    
    682
    +
    
    683
    +
    
    684
    +@dataclass
    
    685
    +class MUCNicknameChanged(ApplicationEvent):
    
    686
    +    name: str = field(init=False, default='muc-nickname-changed')
    
    687
    +    is_self: bool
    
    688
    +    new_name: str
    
    689
    +    old_name: str
    
    690
    +    timestamp: float
    
    691
    +
    
    692
    +
    
    693
    +@dataclass
    
    694
    +class MUCRoomConfigChanged(ApplicationEvent):
    
    695
    +    name: str = field(init=False, default='muc-room-config-changed')
    
    696
    +    timestamp: float
    
    697
    +    status_codes: set[StatusCode]
    
    698
    +
    
    699
    +
    
    700
    +@dataclass
    
    701
    +class MUCRoomConfigFinished(ApplicationEvent):
    
    702
    +    name: str = field(init=False, default='muc-room-config-finished')
    
    703
    +    timestamp: float
    
    704
    +
    
    705
    +
    
    706
    +@dataclass
    
    707
    +class MUCRoomPresenceError(ApplicationEvent):
    
    708
    +    name: str = field(init=False, default='muc-room-presence-error')
    
    709
    +    timestamp: float
    
    710
    +    error: Any
    
    711
    +
    
    712
    +
    
    713
    +@dataclass
    
    714
    +class MUCRoomKicked(ApplicationEvent):
    
    715
    +    name: str = field(init=False, default='muc-room-kicked')
    
    716
    +    timestamp: float
    
    717
    +    status_codes: Optional[set[StatusCode]]
    
    718
    +    reason: Optional[str]
    
    719
    +    actor: Optional[str]
    
    720
    +
    
    721
    +
    
    722
    +@dataclass
    
    723
    +class MUCRoomDestroyed(ApplicationEvent):
    
    724
    +    name: str = field(init=False, default='muc-room-destroyed')
    
    725
    +    timestamp: float
    
    726
    +    reason: Optional[str]
    
    727
    +    alternate: Optional[JID]
    
    728
    +
    
    729
    +
    
    730
    +@dataclass
    
    731
    +class MUCUserJoined(ApplicationEvent):
    
    732
    +    name: str = field(init=False, default='muc-user-joined')
    
    733
    +    timestamp: float
    
    734
    +    is_self: bool
    
    735
    +    nick: str
    
    736
    +    status_codes: Optional[set[StatusCode]]
    
    737
    +
    
    738
    +
    
    739
    +@dataclass
    
    740
    +class MUCUserLeft(ApplicationEvent):
    
    741
    +    name: str = field(init=False, default='muc-user-left')
    
    742
    +    timestamp: float
    
    743
    +    is_self: bool
    
    744
    +    nick: str
    
    745
    +    status_codes: Optional[set[StatusCode]]
    
    746
    +    reason: Optional[str]
    
    747
    +    actor: Optional[str]
    
    748
    +
    
    749
    +
    
    750
    +@dataclass
    
    751
    +class MUCUserRoleChanged(ApplicationEvent):
    
    752
    +    name: str = field(init=False, default='muc-user-role-changed')
    
    753
    +    timestamp: float
    
    754
    +    is_self: bool
    
    755
    +    nick: str
    
    756
    +    role: Role
    
    757
    +    reason: Optional[str]
    
    758
    +    actor: Optional[str]
    
    759
    +
    
    760
    +
    
    761
    +@dataclass
    
    762
    +class MUCUserAffiliationChanged(ApplicationEvent):
    
    763
    +    name: str = field(init=False, default='muc-user-affiliation-changed')
    
    764
    +    timestamp: float
    
    765
    +    is_self: bool
    
    766
    +    nick: str
    
    767
    +    affiliation: Affiliation
    
    768
    +    reason: Optional[str]
    
    769
    +    actor: Optional[str]
    
    770
    +
    
    771
    +
    
    772
    +@dataclass
    
    773
    +class MUCUserStatusShowChanged(ApplicationEvent):
    
    774
    +    name: str = field(init=False, default='muc-user-status-show-changed')
    
    775
    +    timestamp: float
    
    776
    +    is_self: bool
    
    777
    +    nick: str
    
    778
    +    status: str
    
    779
    +    show_value: str

  • gajim/common/modules/muc.py
    ... ... @@ -45,6 +45,7 @@
    45 45
     from gi.repository import GLib
    
    46 46
     
    
    47 47
     from gajim.common import app
    
    48
    +from gajim.common import events
    
    48 49
     from gajim.common import helpers
    
    49 50
     from gajim.common import types
    
    50 51
     from gajim.common.const import ClientState, KindConstant
    
    ... ... @@ -460,7 +461,11 @@ def _on_disco_result_after_config(self, task: Task) -> None:
    460 461
             self._log.info('Configuration finished: %s', jid)
    
    461 462
     
    
    462 463
             room = self._get_contact(jid.bare)
    
    463
    -        room.notify('room-config-finished')
    
    464
    +        event = events.MUCRoomConfigFinished(timestamp=time.time())
    
    465
    +
    
    466
    +        assert isinstance(room, GroupchatContact)
    
    467
    +        app.storage.events.store(room, event)
    
    468
    +        room.notify('room-config-finished', event)
    
    464 469
     
    
    465 470
         def update_presence(self) -> None:
    
    466 471
             mucs = self._get_mucs_with_state([MUCJoinedState.JOINED,
    
    ... ... @@ -540,6 +545,11 @@ def _on_error_presence(self,
    540 545
                 self._set_muc_state(room_jid, MUCJoinedState.NOT_JOINED)
    
    541 546
     
    
    542 547
             else:
    
    548
    +            event = events.MUCRoomPresenceError(
    
    549
    +                timestamp=time.time(),
    
    550
    +                error=properties.error)
    
    551
    +            assert isinstance(room, GroupchatContact)
    
    552
    +            app.storage.events.store(room, event)
    
    543 553
                 room.notify('room-presence-error', properties)
    
    544 554
     
    
    545 555
         def _on_muc_user_presence(self,
    
    ... ... @@ -564,7 +574,15 @@ def _on_muc_user_presence(self,
    564 574
                 self._set_muc_state(room_jid, MUCJoinedState.NOT_JOINED)
    
    565 575
                 self._con.get_module('Bookmarks').remove(room_jid)
    
    566 576
                 room.set_not_joined()
    
    567
    -            room.notify('room-destroyed', properties)
    
    577
    +
    
    578
    +            event = events.MUCRoomDestroyed(
    
    579
    +                timestamp=time.time(),
    
    580
    +                reason=properties.muc_destroyed.reason,
    
    581
    +                alternate=properties.muc_destroyed.alternate)
    
    582
    +            assert isinstance(room, GroupchatContact)
    
    583
    +            app.storage.events.store(room, event)
    
    584
    +
    
    585
    +            room.notify('room-destroyed', event)
    
    568 586
                 return
    
    569 587
     
    
    570 588
             if properties.is_nickname_changed:
    
    ... ... @@ -592,10 +610,16 @@ def _on_muc_user_presence(self,
    592 610
                                                        presence,
    
    593 611
                                                        occupant)
    
    594 612
     
    
    595
    -            room.notify('user-nickname-changed',
    
    596
    -                        occupant,
    
    597
    -                        new_occupant,
    
    598
    -                        properties)
    
    613
    +            event = events.MUCNicknameChanged(
    
    614
    +                timestamp=time.time(),
    
    615
    +                is_self=properties.is_muc_self_presence,
    
    616
    +                new_name=new_occupant.name,
    
    617
    +                old_name=occupant.name)
    
    618
    +
    
    619
    +            assert isinstance(room, GroupchatContact)
    
    620
    +            app.storage.events.store(room, event)
    
    621
    +
    
    622
    +            room.notify('user-nickname-changed', event, occupant, new_occupant)
    
    599 623
                 return
    
    600 624
     
    
    601 625
             is_joined = self._is_user_joined(properties.jid)
    
    ... ... @@ -624,7 +648,16 @@ def _on_muc_user_presence(self,
    624 648
             if properties.is_muc_self_presence and properties.is_kicked:
    
    625 649
                 self._set_muc_state(room_jid, MUCJoinedState.NOT_JOINED)
    
    626 650
                 room.set_not_joined()
    
    627
    -            room.notify('room-kicked', properties)
    
    651
    +
    
    652
    +            event = events.MUCRoomKicked(
    
    653
    +                timestamp=time.time(),
    
    654
    +                status_codes=properties.muc_status_codes,
    
    655
    +                reason=properties.muc_user.reason,
    
    656
    +                actor=properties.muc_user.actor)
    
    657
    +            assert isinstance(room, GroupchatContact)
    
    658
    +            app.storage.events.store(room, event)
    
    659
    +            room.notify('room-kicked', event)
    
    660
    +
    
    628 661
                 status_codes = properties.muc_status_codes or []
    
    629 662
                 if StatusCode.REMOVED_SERVICE_SHUTDOWN in status_codes:
    
    630 663
                     self._start_rejoin_timeout(room_jid)
    
    ... ... @@ -652,30 +685,84 @@ def _process_occupant_presence_change(
    652 685
                 presence: MUCPresenceData,
    
    653 686
                 occupant: GroupchatParticipant) -> None:
    
    654 687
     
    
    688
    +        timestamp = time.time()
    
    689
    +
    
    655 690
             if not occupant.is_available and presence.available:
    
    691
    +
    
    692
    +            event = events.MUCUserJoined(
    
    693
    +                timestamp=timestamp,
    
    694
    +                is_self=properties.is_muc_self_presence,
    
    695
    +                nick=occupant.name,
    
    696
    +                status_codes=properties.muc_status_codes)
    
    697
    +
    
    698
    +            if occupant.room.is_joined or properties.is_muc_self_presence:
    
    699
    +                # Don’t store initial presences on join
    
    700
    +                app.storage.events.store(occupant.room, event)
    
    701
    +
    
    656 702
                 occupant.update_presence(presence)
    
    657
    -            occupant.notify('user-joined', properties)
    
    703
    +            occupant.notify('user-joined', event)
    
    658 704
                 return
    
    659 705
     
    
    660 706
             if not presence.available:
    
    707
    +
    
    708
    +            event = events.MUCUserLeft(
    
    709
    +                timestamp=timestamp,
    
    710
    +                is_self=properties.is_muc_self_presence,
    
    711
    +                nick=occupant.name,
    
    712
    +                status_codes=properties.muc_status_codes,
    
    713
    +                reason=properties.muc_user.reason,
    
    714
    +                actor=properties.muc_user.actor)
    
    715
    +
    
    716
    +            app.storage.events.store(occupant.room, event)
    
    661 717
                 occupant.update_presence(presence)
    
    662
    -            occupant.notify('user-left', properties)
    
    718
    +            occupant.notify('user-left', event)
    
    663 719
                 return
    
    664 720
     
    
    665
    -        signals: list[str] = []
    
    721
    +        signals_and_events: list[tuple[str, Any]] = []
    
    722
    +
    
    666 723
             if occupant.affiliation != presence.affiliation:
    
    667
    -            signals.append('user-affiliation-changed')
    
    724
    +
    
    725
    +            event = events.MUCUserAffiliationChanged(
    
    726
    +                timestamp=timestamp,
    
    727
    +                is_self=properties.is_muc_self_presence,
    
    728
    +                nick=occupant.name,
    
    729
    +                affiliation=presence.affiliation,
    
    730
    +                reason=properties.muc_user.reason,
    
    731
    +                actor=properties.muc_user.actor)
    
    732
    +
    
    733
    +            app.storage.events.store(occupant.room, event)
    
    734
    +            signals_and_events.append(('user-affiliation-changed', event))
    
    668 735
     
    
    669 736
             if occupant.role != presence.role:
    
    670
    -            signals.append('user-role-changed')
    
    737
    +
    
    738
    +            event = events.MUCUserRoleChanged(
    
    739
    +                timestamp=timestamp,
    
    740
    +                is_self=properties.is_muc_self_presence,
    
    741
    +                nick=occupant.name,
    
    742
    +                role=presence.role,
    
    743
    +                reason=properties.muc_user.reason,
    
    744
    +                actor=properties.muc_user.actor)
    
    745
    +
    
    746
    +            app.storage.events.store(occupant.room, event)
    
    747
    +            signals_and_events.append(('user-role-changed', event))
    
    671 748
     
    
    672 749
             if (occupant.status != presence.status or
    
    673 750
                     occupant.show != presence.show):
    
    674
    -            signals.append('user-status-show-changed')
    
    751
    +
    
    752
    +            event = events.MUCUserStatusShowChanged(
    
    753
    +                timestamp=timestamp,
    
    754
    +                is_self=properties.is_muc_self_presence,
    
    755
    +                nick=occupant.name,
    
    756
    +                status=properties.status,
    
    757
    +                show_value=properties.show.value)
    
    758
    +
    
    759
    +            app.storage.events.store(occupant, event)
    
    760
    +            app.storage.events.store(occupant.room, event)
    
    761
    +            signals_and_events.append(('user-status-show-changed', event))
    
    675 762
     
    
    676 763
             occupant.update_presence(presence)
    
    677
    -        for signal in signals:
    
    678
    -            occupant.notify(signal, properties)
    
    764
    +        for signal, event in signals_and_events:
    
    765
    +            occupant.notify(signal, event)
    
    679 766
     
    
    680 767
         def _process_user_presence(self,
    
    681 768
                                    properties: PresenceProperties
    
    ... ... @@ -953,12 +1040,21 @@ def _on_config_change(self,
    953 1040
             if not properties.is_muc_config_change:
    
    954 1041
                 return
    
    955 1042
     
    
    956
    -        room_jid = str(properties.muc_jid)
    
    957 1043
             self._log.info('Received config change: %s %s',
    
    958
    -                       room_jid, properties.muc_status_codes)
    
    1044
    +                       properties.muc_jid, properties.muc_status_codes)
    
    959 1045
     
    
    960
    -        room = self._get_contact(room_jid)
    
    961
    -        room.notify('room-config-changed', properties)
    
    1046
    +        assert properties.muc_status_codes is not None
    
    1047
    +        event = events.MUCRoomConfigChanged(
    
    1048
    +            timestamp=time.time(),
    
    1049
    +            status_codes=properties.muc_status_codes)
    
    1050
    +
    
    1051
    +        assert properties.muc_jid is not None
    
    1052
    +        room = self._get_contact(properties.muc_jid)
    
    1053
    +
    
    1054
    +        assert isinstance(room, GroupchatContact)
    
    1055
    +        app.storage.events.store(room, event)
    
    1056
    +
    
    1057
    +        room.notify('room-config-changed', event)
    
    962 1058
     
    
    963 1059
             raise nbxmpp.NodeProcessed
    
    964 1060
     
    

  • gajim/common/storage/base.py
    ... ... @@ -30,11 +30,15 @@
    30 30
     
    
    31 31
     from gi.repository import GLib
    
    32 32
     
    
    33
    +import nbxmpp.const
    
    33 34
     from nbxmpp.protocol import Iq
    
    34 35
     from nbxmpp.protocol import JID
    
    35 36
     from nbxmpp.structs import RosterItem
    
    36 37
     from nbxmpp.structs import DiscoInfo
    
    37 38
     from nbxmpp.structs import CommonError
    
    39
    +from nbxmpp.const import Role
    
    40
    +from nbxmpp.const import Affiliation
    
    41
    +from nbxmpp.const import StatusCode
    
    38 42
     from nbxmpp.modules.discovery import parse_disco_info
    
    39 43
     
    
    40 44
     
    
    ... ... @@ -98,6 +102,13 @@ def _adapt_disco_info(disco_info: DiscoInfo) -> str:
    98 102
     sqlite3.register_adapter(DiscoInfo, _adapt_disco_info)
    
    99 103
     
    
    100 104
     
    
    105
    +def _convert_json(json_string: bytes) -> dict[str, Any]:
    
    106
    +    return json.loads(json_string, object_hook=json_decoder)
    
    107
    +
    
    108
    +
    
    109
    +sqlite3.register_converter('JSON', _convert_json)
    
    110
    +
    
    111
    +
    
    101 112
     class Encoder(json.JSONEncoder):
    
    102 113
         def default(self, o: Any) -> Any:
    
    103 114
             if isinstance(o, set):
    
    ... ... @@ -111,6 +122,10 @@ def default(self, o: Any) -> Any:
    111 122
                 dct['__type'] = 'RosterItem'
    
    112 123
                 return dct
    
    113 124
     
    
    125
    +        if isinstance(o, (Affiliation, Role, StatusCode)):
    
    126
    +            return {'value': o.value,
    
    127
    +                    '__type': o.__class__.__name__}
    
    128
    +
    
    114 129
             return json.JSONEncoder.default(self, o)
    
    115 130
     
    
    116 131
     
    
    ... ... @@ -118,8 +133,10 @@ def json_decoder(dct: dict[str, Any]) -> Any:
    118 133
         type_ = dct.get('__type')
    
    119 134
         if type_ is None:
    
    120 135
             return dct
    
    136
    +
    
    121 137
         if type_ == 'JID':
    
    122 138
             return JID.from_string(dct['value'])
    
    139
    +
    
    123 140
         if type_ == 'RosterItem':
    
    124 141
             return RosterItem(jid=dct['jid'],
    
    125 142
                               name=dct['name'],
    
    ... ... @@ -127,6 +144,10 @@ def json_decoder(dct: dict[str, Any]) -> Any:
    127 144
                               subscription=dct['subscription'],
    
    128 145
                               approved=dct['approved'],
    
    129 146
                               groups=set(dct['groups']))
    
    147
    +
    
    148
    +    if type_ in ('Affiliation', 'Role', 'StatusCode'):
    
    149
    +        return getattr(nbxmpp.const, type_)(dct['value'])
    
    150
    +
    
    130 151
         return dct
    
    131 152
     
    
    132 153
     
    

  • gajim/common/storage/events.py
    1
    +# This file is part of Gajim.
    
    2
    +#
    
    3
    +# Gajim is free software; you can redistribute it and/or modify
    
    4
    +# it under the terms of the GNU General Public License as published
    
    5
    +# by the Free Software Foundation; version 3 only.
    
    6
    +#
    
    7
    +# Gajim is distributed in the hope that it will be useful,
    
    8
    +# but WITHOUT ANY WARRANTY; without even the implied warranty of
    
    9
    +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    
    10
    +# GNU General Public License for more details.
    
    11
    +#
    
    12
    +# You should have received a copy of the GNU General Public License
    
    13
    +# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
    
    14
    +
    
    15
    +from __future__ import annotations
    
    16
    +import dataclasses
    
    17
    +
    
    18
    +from typing import Any
    
    19
    +from typing import Iterator
    
    20
    +from typing import NamedTuple
    
    21
    +
    
    22
    +import json
    
    23
    +import sqlite3
    
    24
    +import logging
    
    25
    +from collections import namedtuple
    
    26
    +
    
    27
    +from nbxmpp.protocol import JID
    
    28
    +
    
    29
    +from gajim.common import events
    
    30
    +from gajim.common.storage.base import SqliteStorage
    
    31
    +from gajim.common.storage.base import Encoder
    
    32
    +from gajim.common.types import ChatContactT
    
    33
    +
    
    34
    +
    
    35
    +EVENTS_SQL_STATEMENT = '''
    
    36
    +    CREATE TABLE events (
    
    37
    +            account TEXT,
    
    38
    +            jid TEXT,
    
    39
    +            event TEXT,
    
    40
    +            timestamp REAL,
    
    41
    +            data TEXT);
    
    42
    +
    
    43
    +    CREATE INDEX idx_account_jid ON events(account, jid);
    
    44
    +
    
    45
    +    PRAGMA user_version=1;
    
    46
    +    '''
    
    47
    +
    
    48
    +EVENT_CLASSES: dict[str, Any] = {
    
    49
    +    'muc-nickname-changed': events.MUCNicknameChanged,
    
    50
    +    'muc-room-config-changed': events.MUCRoomConfigChanged,
    
    51
    +    'muc-room-config-finished': events.MUCRoomConfigFinished,
    
    52
    +    'muc-room-presence-error': events.MUCRoomPresenceError,
    
    53
    +    'muc-room-kicked': events.MUCRoomKicked,
    
    54
    +    'muc-room-destroyed': events.MUCRoomDestroyed,
    
    55
    +    'muc-user-joined': events.MUCUserJoined,
    
    56
    +    'muc-user-left': events.MUCUserLeft,
    
    57
    +    'muc-user-role-changed': events.MUCUserRoleChanged,
    
    58
    +    'muc-user-affiliation-changed': events.MUCUserAffiliationChanged,
    
    59
    +    'muc-user-status-show-changed': events.MUCUserStatusShowChanged,
    
    60
    +}
    
    61
    +
    
    62
    +log = logging.getLogger('gajim.c.storage.events')
    
    63
    +
    
    64
    +
    
    65
    +class EventRow(NamedTuple):
    
    66
    +    account: str
    
    67
    +    jid: JID
    
    68
    +    event: str
    
    69
    +    timestamp: float
    
    70
    +    data: dict[str, Any]
    
    71
    +
    
    72
    +
    
    73
    +class EventStorage(SqliteStorage):
    
    74
    +    def __init__(self):
    
    75
    +        SqliteStorage.__init__(self,
    
    76
    +                               log,
    
    77
    +                               None,
    
    78
    +                               EVENTS_SQL_STATEMENT)
    
    79
    +
    
    80
    +    def init(self, **kwargs: Any) -> None:
    
    81
    +        SqliteStorage.init(self,
    
    82
    +                           detect_types=sqlite3.PARSE_COLNAMES)
    
    83
    +        self._con.row_factory = self._namedtuple_factory
    
    84
    +
    
    85
    +    @staticmethod
    
    86
    +    def _namedtuple_factory(cursor: sqlite3.Cursor,
    
    87
    +                            row: tuple[Any, ...]) -> NamedTuple:
    
    88
    +
    
    89
    +        assert cursor.description is not None
    
    90
    +        fields = [col[0] for col in cursor.description]
    
    91
    +        Row = namedtuple('Row', fields)  # type: ignore
    
    92
    +        return Row(*row)
    
    93
    +
    
    94
    +    def _migrate(self) -> None:
    
    95
    +        pass
    
    96
    +
    
    97
    +    def store(self,
    
    98
    +              contact: ChatContactT,
    
    99
    +              event: Any
    
    100
    +              ) -> None:
    
    101
    +
    
    102
    +        event_dict = dataclasses.asdict(event)
    
    103
    +        name = event_dict.pop('name')
    
    104
    +        timestamp = event_dict.pop('timestamp')
    
    105
    +
    
    106
    +        insert_sql = '''
    
    107
    +            INSERT INTO events(account, jid, event, timestamp, data)
    
    108
    +            VALUES(?, ?, ?, ?, ?)'''
    
    109
    +
    
    110
    +        self._con.execute(insert_sql, (contact.account,
    
    111
    +                                       contact.jid,
    
    112
    +                                       name,
    
    113
    +                                       timestamp,
    
    114
    +                                       json.dumps(event_dict, cls=Encoder)))
    
    115
    +
    
    116
    +    def load(self,
    
    117
    +             contact: ChatContactT,
    
    118
    +             before: bool,
    
    119
    +             timestamp: float
    
    120
    +             ) -> Iterator[EventRow]:
    
    121
    +
    
    122
    +        time_operator = '<' if before else '>'
    
    123
    +
    
    124
    +        insert_sql = '''
    
    125
    +            SELECT account, jid, event, timestamp, data as "data [JSON]"
    
    126
    +            FROM events WHERE account=? AND jid=? AND timestamp %s ?
    
    127
    +            ''' % time_operator
    
    128
    +
    
    129
    +        for row in self._con.execute(insert_sql, (contact.account,
    
    130
    +                                                  contact.jid,
    
    131
    +                                                  timestamp)).fetchall():
    
    132
    +            event_class = EVENT_CLASSES[row.event]
    
    133
    +            yield event_class(**row.data, timestamp=row.timestamp)

  • gajim/gtk/chat_banner.py
    ... ... @@ -21,8 +21,6 @@
    21 21
     
    
    22 22
     import cairo
    
    23 23
     
    
    24
    -from nbxmpp.structs import PresenceProperties
    
    25
    -
    
    26 24
     from gajim.common import app
    
    27 25
     from gajim.common import ged
    
    28 26
     from gajim.common import types
    
    ... ... @@ -195,13 +193,7 @@ def _on_room_voice_request(self, *args: Any) -> None:
    195 193
             self._voice_requests_button.set_no_show_all(False)
    
    196 194
             self._voice_requests_button.show_all()
    
    197 195
     
    
    198
    -    def _on_user_role_changed(self,
    
    199
    -                              _contact: GroupchatContact,
    
    200
    -                              _signal_name: str,
    
    201
    -                              user_contact: GroupchatParticipant,
    
    202
    -                              properties: PresenceProperties
    
    203
    -                              ) -> None:
    
    204
    -
    
    196
    +    def _on_user_role_changed(self, *args: Any) -> None:
    
    205 197
             self._update_content()
    
    206 198
     
    
    207 199
         def _on_user_state_changed(self, *args: Any) -> None:
    

  • gajim/gtk/chat_stack.py
    ... ... @@ -28,7 +28,6 @@
    28 28
     from nbxmpp.errors import StanzaError
    
    29 29
     from nbxmpp.protocol import JID
    
    30 30
     from nbxmpp.structs import MessageProperties
    
    31
    -from nbxmpp.structs import PresenceProperties
    
    32 31
     
    
    33 32
     from gajim.common import app
    
    34 33
     from gajim.common import events
    
    ... ... @@ -297,23 +296,25 @@ def _on_user_joined(self,
    297 296
                             contact: GroupchatContact,
    
    298 297
                             _signal_name: str,
    
    299 298
                             _user_contact: GroupchatParticipant,
    
    300
    -                        _properties: PresenceProperties
    
    299
    +                        _event: events.MUCUserJoined
    
    301 300
                             ) -> None:
    
    301
    +
    
    302 302
             self._update_group_chat_actions(contact)
    
    303 303
     
    
    304 304
         def _on_user_role_changed(self,
    
    305 305
                                   contact: GroupchatContact,
    
    306 306
                                   _signal_name: str,
    
    307 307
                                   _user_contact: GroupchatParticipant,
    
    308
    -                              _properties: PresenceProperties
    
    308
    +                              _event: events.MUCUserRoleChanged
    
    309 309
                                   ) -> None:
    
    310
    +
    
    310 311
             self._update_group_chat_actions(contact)
    
    311 312
     
    
    312 313
         def _on_user_affiliation_changed(self,
    
    313 314
                                          contact: GroupchatContact,
    
    314 315
                                          _signal_name: str,
    
    315 316
                                          _user_contact: GroupchatParticipant,
    
    316
    -                                     _properties: PresenceProperties
    
    317
    +                                     _event: events.MUCUserAffiliationChanged
    
    317 318
                                          ) -> None:
    
    318 319
             self._update_group_chat_actions(contact)
    
    319 320
     
    

  • gajim/gtk/control.py
    ... ... @@ -19,9 +19,6 @@
    19 19
     from typing import Union
    
    20 20
     from typing import cast
    
    21 21
     
    
    22
    -from collections import defaultdict
    
    23
    -from collections import deque
    
    24
    -from functools import partial
    
    25 22
     import os
    
    26 23
     import logging
    
    27 24
     import time
    
    ... ... @@ -31,9 +28,7 @@
    31 28
     
    
    32 29
     from nbxmpp import JID
    
    33 30
     from nbxmpp.const import StatusCode
    
    34
    -from nbxmpp.structs import MessageProperties
    
    35 31
     from nbxmpp.structs import MucSubject
    
    36
    -from nbxmpp.structs import PresenceProperties
    
    37 32
     from nbxmpp.modules.security_labels import Displaymarking
    
    38 33
     
    
    39 34
     from gajim.common import app
    
    ... ... @@ -100,12 +95,6 @@ def __init__(self) -> None:
    100 95
             self._muc_subjects: dict[
    
    101 96
                 types.ChatContactT, tuple[float, MucSubject]] = {}
    
    102 97
     
    
    103
    -        # Store 100 info messages per contact on a FIFO basis
    
    104
    -        self._info_messages: dict[
    
    105
    -            types.ChatContactT,
    
    106
    -            deque[tuple[float, str]]] = defaultdict(
    
    107
    -                partial(deque, maxlen=100))  # pyright: ignore
    
    108
    -
    
    109 98
             self.widget = cast(Gtk.Box, self._ui.get_object('control_box'))
    
    110 99
             self.widget.show_all()
    
    111 100
     
    
    ... ... @@ -177,7 +166,8 @@ def switch_contact(self, contact: Union[BareContact,
    177 166
     
    
    178 167
             if isinstance(contact, GroupchatParticipant):
    
    179 168
                 contact.multi_connect({
    
    180
    -                'user-status-show-changed': self._on_user_status_show_changed,
    
    169
    +                'user-status-show-changed':
    
    170
    +                    self._on_participant_status_show_changed,
    
    181 171
                 })
    
    182 172
     
    
    183 173
             elif isinstance(contact, GroupchatContact):
    
    ... ... @@ -186,8 +176,7 @@ def switch_contact(self, contact: Union[BareContact,
    186 176
                     'user-left': self._on_user_left,
    
    187 177
                     'user-affiliation-changed': self._on_user_affiliation_changed,
    
    188 178
                     'user-role-changed': self._on_user_role_changed,
    
    189
    -                'user-status-show-changed':
    
    190
    -                    self._on_muc_user_status_show_changed,
    
    179
    +                'user-status-show-changed': self._on_user_status_show_changed,
    
    191 180
                     'user-nickname-changed': self._on_user_nickname_changed,
    
    192 181
                     'room-kicked': self._on_room_kicked,
    
    193 182
                     'room-destroyed': self._on_room_destroyed,
    
    ... ... @@ -205,9 +194,6 @@ def switch_contact(self, contact: Union[BareContact,
    205 194
                 for transfer in transfers:
    
    206 195
                     self.add_file_transfer(transfer)
    
    207 196
     
    
    208
    -        for timestamp, message in self._info_messages[contact]:
    
    209
    -            self.add_info_message(message, timestamp)
    
    210
    -
    
    211 197
             if isinstance(contact, GroupchatContact):
    
    212 198
                 if (app.settings.get('show_subject_on_join') or
    
    213 199
                         not contact.is_joining):
    
    ... ... @@ -501,10 +487,6 @@ def add_info_message(self,
    501 487
                              timestamp: Optional[float] = None
    
    502 488
                              ) -> None:
    
    503 489
     
    
    504
    -        if timestamp is None:
    
    505
    -            assert self._contact is not None
    
    506
    -            self._info_messages[self._contact].append((time.time(), text))
    
    507
    -
    
    508 490
             self.conversation_view.add_info_message(text, timestamp)
    
    509 491
     
    
    510 492
         def add_file_transfer(self, transfer: HTTPFileTransfer) -> None:
    
    ... ... @@ -619,6 +601,43 @@ def _fetch_n_lines_history(self,
    619 601
             #    if self.conversation_view.reduce_message_count(before):
    
    620 602
             #        self._scrolled_view.set_history_complete(before, False)
    
    621 603
     
    
    604
    +        assert self._contact is not None
    
    605
    +        for event in app.storage.events.load(self._contact, before, timestamp):
    
    606
    +            if isinstance(event, events.MUCUserJoined):
    
    607
    +                self._process_muc_user_joined(event)
    
    608
    +
    
    609
    +            elif isinstance(event, events.MUCUserLeft):
    
    610
    +                self._process_muc_user_left(event)
    
    611
    +
    
    612
    +            elif isinstance(event, events.MUCNicknameChanged):
    
    613
    +                self._process_muc_nickname_changed(event)
    
    614
    +
    
    615
    +            elif isinstance(event, events.MUCRoomKicked):
    
    616
    +                self._process_muc_room_kicked(event)
    
    617
    +
    
    618
    +            elif isinstance(event, events.MUCUserAffiliationChanged):
    
    619
    +                self._process_muc_user_affiliation_changed(event)
    
    620
    +
    
    621
    +            elif isinstance(event, events.MUCUserRoleChanged):
    
    622
    +                self._process_muc_user_role_changed(event)
    
    623
    +
    
    624
    +            elif isinstance(event, events.MUCUserStatusShowChanged):
    
    625
    +                self._process_muc_user_status_show_changed(event)
    
    626
    +
    
    627
    +            elif isinstance(event, events.MUCRoomConfigChanged):
    
    628
    +                self._process_muc_room_config_changed(event)
    
    629
    +
    
    630
    +            elif isinstance(event, events.MUCRoomConfigFinished):
    
    631
    +                self._process_muc_room_config_finished(event)
    
    632
    +
    
    633
    +            elif isinstance(event, events.MUCRoomPresenceError):
    
    634
    +                self._process_muc_room_presence_error(event)
    
    635
    +
    
    636
    +            elif isinstance(event, events.MUCRoomDestroyed):
    
    637
    +                self._process_muc_room_destroyed(event)
    
    638
    +            else:
    
    639
    +                raise ValueError('Unknown event: %s' % event)
    
    640
    +
    
    622 641
             self._scrolled_view.block_signals(False)
    
    623 642
     
    
    624 643
         def add_messages(self, messages: list[ConversationRow]):
    
    ... ... @@ -703,120 +722,228 @@ def add_message(self,
    703 722
                               additional_data=additional_data)
    
    704 723
     
    
    705 724
         def _on_user_nickname_changed(self,
    
    706
    -                                  contact: types.GroupchatContact,
    
    725
    +                                  _contact: types.GroupchatContact,
    
    707 726
                                       _signal_name: str,
    
    708
    -                                  old_contact: types.GroupchatParticipant,
    
    709
    -                                  new_contact: types.GroupchatParticipant,
    
    710
    -                                  properties: PresenceProperties
    
    727
    +                                  event: events.MUCNicknameChanged,
    
    728
    +                                  _old_contact: types.GroupchatParticipant,
    
    729
    +                                  _new_contact: types.GroupchatParticipant
    
    711 730
                                       ) -> None:
    
    712 731
     
    
    713
    -        if properties.is_muc_self_presence:
    
    714
    -            message = _('You are now known as %s') % new_contact.name
    
    732
    +        self._process_muc_nickname_changed(event)
    
    733
    +
    
    734
    +    def _process_muc_nickname_changed(self,
    
    735
    +                                      event: events.MUCNicknameChanged
    
    736
    +                                      ) -> None:
    
    737
    +
    
    738
    +        if event.is_self:
    
    739
    +            message = _('You are now known as %s') % event.new_name
    
    715 740
             else:
    
    716 741
                 message = _('{nick} is now known '
    
    717
    -                        'as {new_nick}').format(nick=old_contact.name,
    
    718
    -                                                new_nick=new_contact.name)
    
    742
    +                        'as {new_nick}').format(nick=event.old_name,
    
    743
    +                                                new_nick=event.new_name)
    
    744
    +        self.add_info_message(message, event.timestamp)
    
    719 745
     
    
    720
    -        self.add_info_message(message)
    
    746
    +    def _on_room_kicked(self,
    
    747
    +                        _contact: GroupchatContact,
    
    748
    +                        _signal_name: str,
    
    749
    +                        event: events.MUCRoomKicked
    
    750
    +                        ) -> None:
    
    721 751
     
    
    722
    -    def _on_muc_user_status_show_changed(self,
    
    723
    -                                         contact: GroupchatContact,
    
    724
    -                                         _signal_name: str,
    
    725
    -                                         user_contact: GroupchatParticipant,
    
    726
    -                                         properties: PresenceProperties
    
    727
    -                                         ) -> None:
    
    752
    +        self._process_muc_room_kicked(event)
    
    728 753
     
    
    729
    -        if not contact.settings.get('print_status'):
    
    754
    +    def _process_muc_room_kicked(self, event: events.MUCRoomKicked) -> None:
    
    755
    +        status_codes = event.status_codes or []
    
    756
    +
    
    757
    +        reason = event.reason
    
    758
    +        reason = '' if reason is None else f': {reason}'
    
    759
    +
    
    760
    +        actor = event.actor
    
    761
    +        # Group Chat: You have been kicked by Alice
    
    762
    +        actor = '' if actor is None else _(' by {actor}').format(
    
    763
    +            actor=actor)
    
    764
    +
    
    765
    +        # Group Chat: We have been removed from the room by Alice: reason
    
    766
    +        message = _('You have been removed from the '
    
    767
    +                    'group chat{actor}{reason}')
    
    768
    +
    
    769
    +        if StatusCode.REMOVED_ERROR in status_codes:
    
    770
    +            # Handle 333 before 307, some MUCs add both
    
    771
    +            # Group Chat: Server kicked us because of an server error
    
    772
    +            message = _('You have left due '
    
    773
    +                        'to an error{reason}').format(reason=reason)
    
    774
    +
    
    775
    +        elif StatusCode.REMOVED_KICKED in status_codes:
    
    776
    +            # Group Chat: We have been kicked by Alice: reason
    
    777
    +            message = _('You have been '
    
    778
    +                        'kicked{actor}{reason}').format(actor=actor,
    
    779
    +                                                        reason=reason)
    
    780
    +
    
    781
    +        elif StatusCode.REMOVED_BANNED in status_codes:
    
    782
    +            # Group Chat: We have been banned by Alice: reason
    
    783
    +            message = _('You have been '
    
    784
    +                        'banned{actor}{reason}').format(actor=actor,
    
    785
    +                                                        reason=reason)
    
    786
    +
    
    787
    +        elif StatusCode.REMOVED_AFFILIATION_CHANGE in status_codes:
    
    788
    +            # Group Chat: We were removed because of an affiliation change
    
    789
    +            reason = _(': Affiliation changed')
    
    790
    +            message = message.format(actor=actor, reason=reason)
    
    791
    +
    
    792
    +        elif StatusCode.REMOVED_NONMEMBER_IN_MEMBERS_ONLY in status_codes:
    
    793
    +            # Group Chat: Room configuration changed
    
    794
    +            reason = _(': Group chat configuration changed to members-only')
    
    795
    +            message = message.format(actor=actor, reason=reason)
    
    796
    +
    
    797
    +        elif StatusCode.REMOVED_SERVICE_SHUTDOWN in status_codes:
    
    798
    +            # Group Chat: Kicked because of server shutdown
    
    799
    +            reason = ': System shutdown'
    
    800
    +            message = message.format(actor=actor, reason=reason)
    
    801
    +
    
    802
    +        else:
    
    803
    +            # No formatted message available
    
    730 804
                 return
    
    731 805
     
    
    732
    -        self.conversation_view.add_user_status(user_contact.name,
    
    733
    -                                               user_contact.show.value,
    
    734
    -                                               user_contact.status)
    
    806
    +        self.add_info_message(message, event.timestamp)
    
    735 807
     
    
    736
    -    def _on_user_status_show_changed(self,
    
    737
    -                                     _user_contact: GroupchatParticipant,
    
    808
    +    def _on_user_affiliation_changed(self,
    
    809
    +                                     _contact: GroupchatContact,
    
    738 810
                                          _signal_name: str,
    
    739
    -                                     properties: PresenceProperties
    
    811
    +                                     user_contact: GroupchatParticipant,
    
    812
    +                                     event: events.MUCUserAffiliationChanged
    
    740 813
                                          ) -> None:
    
    741 814
     
    
    742
    -        nick = properties.muc_nickname
    
    743
    -        status = properties.status
    
    744
    -        status = '' if not status else f' - {status}'
    
    745
    -        assert properties.show is not None
    
    746
    -        show = helpers.get_uf_show(properties.show.value)
    
    815
    +        self._process_muc_user_affiliation_changed(event)
    
    747 816
     
    
    748
    -        assert isinstance(self.contact, GroupchatParticipant)
    
    749
    -        if not self.contact.room.settings.get('print_status'):
    
    750
    -            return
    
    817
    +    def _process_muc_user_affiliation_changed(
    
    818
    +            self,
    
    819
    +            event: events.MUCUserAffiliationChanged) -> None:
    
    751 820
     
    
    752
    -        if properties.is_muc_self_presence:
    
    753
    -            message = _('You are now {show}{status}').format(show=show,
    
    754
    -                                                             status=status)
    
    821
    +        affiliation = helpers.get_uf_affiliation(event.affiliation)
    
    822
    +
    
    823
    +        reason = event.reason
    
    824
    +        reason = '' if reason is None else f': {reason}'
    
    825
    +
    
    826
    +        actor = event.actor
    
    827
    +        # Group Chat: You have been kicked by Alice
    
    828
    +        actor = '' if actor is None else _(' by {actor}').format(
    
    829
    +            actor=actor)
    
    755 830
     
    
    831
    +        if event.is_self:
    
    832
    +            message = _('** Your Affiliation has been set to '
    
    833
    +                        '{affiliation}{actor}{reason}').format(
    
    834
    +                            affiliation=affiliation,
    
    835
    +                            actor=actor,
    
    836
    +                            reason=reason)
    
    756 837
             else:
    
    757
    -            message = _('{nick} is now {show}{status}').format(nick=nick,
    
    758
    -                                                               show=show,
    
    759
    -                                                               status=status)
    
    760
    -        self.add_info_message(message)
    
    838
    +            message = _('** Affiliation of {nick} has been set to '
    
    839
    +                        '{affiliation}{actor}{reason}').format(
    
    840
    +                            nick=event.nick,
    
    841
    +                            affiliation=affiliation,
    
    842
    +                            actor=actor,
    
    843
    +                            reason=reason)
    
    761 844
     
    
    762
    -    def add_muc_message(self,
    
    763
    -                        text: str,
    
    764
    -                        tim: float,
    
    765
    -                        contact: str = '',
    
    766
    -                        displaymarking: Optional[Displaymarking] = None,
    
    767
    -                        message_id: Optional[str] = None,
    
    768
    -                        stanza_id: Optional[str] = None,
    
    769
    -                        additional_data: Optional[AdditionalDataDict] = None,
    
    770
    -                        ) -> None:
    
    845
    +        self.add_info_message(message, event.timestamp)
    
    771 846
     
    
    772
    -        assert isinstance(self._contact, GroupchatContact)
    
    847
    +    def _on_user_role_changed(self,
    
    848
    +                              _contact: GroupchatContact,
    
    849
    +                              _signal_name: str,
    
    850
    +                              user_contact: GroupchatParticipant,
    
    851
    +                              event: events.MUCUserRoleChanged
    
    852
    +                              ) -> None:
    
    773 853
     
    
    774
    -        if contact == self._contact.nickname:
    
    775
    -            kind = 'outgoing'
    
    854
    +        self._process_muc_user_role_changed(event)
    
    855
    +
    
    856
    +    def _process_muc_user_role_changed(self,
    
    857
    +                                       event: events.MUCUserRoleChanged
    
    858
    +                                       ) -> None:
    
    859
    +
    
    860
    +        role = helpers.get_uf_role(event.role)
    
    861
    +        nick = event.nick
    
    862
    +
    
    863
    +        reason = event.reason
    
    864
    +        reason = '' if reason is None else f': {reason}'
    
    865
    +
    
    866
    +        actor = event.actor
    
    867
    +        # Group Chat: You have been kicked by Alice
    
    868
    +        actor = '' if actor is None else _(' by {actor}').format(actor=actor)
    
    869
    +
    
    870
    +        if event.is_self:
    
    871
    +            message = _('** Your Role has been set to '
    
    872
    +                        '{role}{actor}{reason}').format(role=role,
    
    873
    +                                                        actor=actor,
    
    874
    +                                                        reason=reason)
    
    776 875
             else:
    
    777
    -            kind = 'incoming'
    
    778
    -            # muc-specific chatstate
    
    876
    +            message = _('** Role of {nick} has been set to '
    
    877
    +                        '{role}{actor}{reason}').format(nick=nick,
    
    878
    +                                                        role=role,
    
    879
    +                                                        actor=actor,
    
    880
    +                                                        reason=reason)
    
    881
    +        self.add_info_message(message, event.timestamp)
    
    779 882
     
    
    780
    -        self._add_message(text,
    
    781
    -                          kind,
    
    782
    -                          contact,
    
    783
    -                          tim,
    
    784
    -                          displaymarking=displaymarking,
    
    785
    -                          message_id=message_id,
    
    786
    -                          stanza_id=stanza_id,
    
    787
    -                          additional_data=additional_data)
    
    883
    +    def _on_user_status_show_changed(self,
    
    884
    +                                     contact: GroupchatContact,
    
    885
    +                                     _signal_name: str,
    
    886
    +                                     _user_contact: GroupchatParticipant,
    
    887
    +                                     event: events.MUCUserStatusShowChanged
    
    888
    +                                     ) -> None:
    
    788 889
     
    
    789
    -    def _on_room_subject(self,
    
    790
    -                         contact: GroupchatContact,
    
    791
    -                         _signal_name: str,
    
    792
    -                         subject: Optional[MucSubject]
    
    793
    -                         ) -> None:
    
    890
    +        self._process_muc_user_status_show_changed(event)
    
    794 891
     
    
    795
    -        if subject is None:
    
    796
    -            return
    
    892
    +    def _on_participant_status_show_changed(
    
    893
    +            self,
    
    894
    +            contact: GroupchatParticipant,
    
    895
    +            _signal_name: str,
    
    896
    +            event: events.MUCUserStatusShowChanged) -> None:
    
    797 897
     
    
    798
    -        _timestamp, old_subject = self._muc_subjects.get(contact, (None, None))
    
    799
    -        if old_subject is not None and old_subject.text == subject.text:
    
    800
    -            # Probably a rejoin, we already showed that subject
    
    898
    +        self._process_muc_user_status_show_changed(event)
    
    899
    +
    
    900
    +    def _process_muc_user_status_show_changed(
    
    901
    +            self,
    
    902
    +            event: events.MUCUserStatusShowChanged) -> None:
    
    903
    +
    
    904
    +        if isinstance(self._contact, GroupchatContact):
    
    905
    +            contact = self._contact
    
    906
    +        elif isinstance(self._contact, GroupchatParticipant):
    
    907
    +            contact = self._contact.room
    
    908
    +        else:
    
    909
    +            raise AssertionError
    
    910
    +
    
    911
    +        if not contact.settings.get('print_status'):
    
    801 912
                 return
    
    802 913
     
    
    803
    -        self._muc_subjects[contact] = (time.time(), subject)
    
    914
    +        nick = event.nick
    
    915
    +        status = event.status
    
    916
    +        status = '' if not status else f' - {status}'
    
    917
    +        show = helpers.get_uf_show(event.show_value)
    
    804 918
     
    
    805
    -        if (app.settings.get('show_subject_on_join') or
    
    806
    -                not contact.is_joining):
    
    807
    -            self.conversation_view.add_muc_subject(subject)
    
    919
    +        if event.is_self:
    
    920
    +            message = _('You are now {show}{status}').format(show=show,
    
    921
    +                                                             status=status)
    
    922
    +
    
    923
    +        else:
    
    924
    +            message = _('{nick} is now {show}{status}').format(
    
    925
    +                nick=nick,
    
    926
    +                show=show,
    
    927
    +                status=status)
    
    928
    +
    
    929
    +        self.add_info_message(message, event.timestamp)
    
    808 930
     
    
    809 931
         def _on_room_config_changed(self,
    
    810 932
                                     _contact: GroupchatContact,
    
    811 933
                                     _signal_name: str,
    
    812
    -                                properties: MessageProperties
    
    934
    +                                event: events.MUCRoomConfigChanged
    
    813 935
                                     ) -> None:
    
    814
    -        # http://www.xmpp.org/extensions/xep-0045.html#roomconfig-notify
    
    815 936
     
    
    816
    -        status_codes = properties.muc_status_codes
    
    817
    -        assert status_codes is not None
    
    937
    +        self._process_muc_room_config_changed(event)
    
    938
    +
    
    939
    +    def _process_muc_room_config_changed(self,
    
    940
    +                                         event: events.MUCRoomConfigChanged
    
    941
    +                                         ) -> None:
    
    818 942
     
    
    943
    +        # http://www.xmpp.org/extensions/xep-0045.html#roomconfig-notify
    
    944
    +        status_codes = event.status_codes
    
    819 945
             changes: list[str] = []
    
    946
    +
    
    820 947
             if StatusCode.SHOWING_UNAVAILABLE in status_codes:
    
    821 948
                 changes.append(_('Group chat now shows unavailable members'))
    
    822 949
     
    
    ... ... @@ -830,7 +957,8 @@ def _on_room_config_changed(self,
    830 957
                 self.client.get_module('Discovery').disco_muc(self.contact.jid)
    
    831 958
     
    
    832 959
             if StatusCode.CONFIG_ROOM_LOGGING in status_codes:
    
    833
    -            # Can be a presence (see chg_contact_status in groupchat_control.py)
    
    960
    +            # Can be a presence
    
    961
    +            # (see chg_contact_status in groupchat_control.py)
    
    834 962
                 changes.append(_('Conversations are stored on the server'))
    
    835 963
     
    
    836 964
             if StatusCode.CONFIG_NO_ROOM_LOGGING in status_codes:
    
    ... ... @@ -845,252 +973,202 @@ def _on_room_config_changed(self,
    845 973
             if StatusCode.CONFIG_FULL_ANONYMOUS in status_codes:
    
    846 974
                 changes.append(_('Group chat is now fully anonymous'))
    
    847 975
     
    
    848
    -        for change in changes:
    
    849
    -            self.add_info_message(change)
    
    850
    -
    
    851
    -    def rejoin(self) -> None:
    
    852
    -        self.client.get_module('MUC').join(self.contact.jid)
    
    853
    -
    
    854
    -    def _on_user_joined(self,
    
    855
    -                        contact: GroupchatContact,
    
    856
    -                        _signal_name: str,
    
    857
    -                        user_contact: GroupchatParticipant,
    
    858
    -                        properties: PresenceProperties
    
    859
    -                        ) -> None:
    
    860
    -
    
    861
    -        nick = user_contact.name
    
    862
    -        if not properties.is_muc_self_presence:
    
    863
    -            if contact.is_joined:
    
    864
    -                self.conversation_view.add_muc_user_joined(nick)
    
    865
    -            return
    
    866
    -
    
    867
    -        status_codes = properties.muc_status_codes or []
    
    868
    -
    
    869
    -        if not contact.is_joined:
    
    870
    -            # We just joined the room
    
    871
    -            self.add_info_message(_('You (%s) joined the group chat') % nick)
    
    872
    -
    
    873
    -        if StatusCode.NON_ANONYMOUS in status_codes:
    
    874
    -            self.add_info_message(
    
    875
    -                _('Any participant is allowed to see your full XMPP Address'))
    
    876
    -
    
    877
    -        if StatusCode.CONFIG_ROOM_LOGGING in status_codes:
    
    878
    -            self.add_info_message(_('Conversations are stored on the server'))
    
    879
    -
    
    880
    -        if StatusCode.NICKNAME_MODIFIED in status_codes:
    
    881
    -            self.add_info_message(
    
    882
    -                _('The server has assigned or modified your nickname in this '
    
    883
    -                  'group chat'))
    
    976
    +        for message in changes:
    
    977
    +            self.add_info_message(message, event.timestamp)
    
    884 978
     
    
    885 979
         def _on_room_config_finished(self,
    
    886 980
                                      _contact: GroupchatContact,
    
    887
    -                                 _signal_name: str
    
    981
    +                                 _signal_name: str,
    
    982
    +                                 event: events.MUCRoomConfigFinished
    
    888 983
                                      ) -> None:
    
    984
    +        self._process_muc_room_config_finished(event)
    
    985
    +
    
    986
    +    def _process_muc_room_config_finished(self,
    
    987
    +                                          event: events.MUCRoomConfigFinished
    
    988
    +                                          ) -> None:
    
    989
    +
    
    889 990
             self.add_info_message(_('A new group chat has been created'))
    
    890 991
     
    
    891
    -    def _on_user_affiliation_changed(self,
    
    892
    -                                     _contact: GroupchatContact,
    
    893
    -                                     _signal_name: str,
    
    894
    -                                     user_contact: GroupchatParticipant,
    
    895
    -                                     properties: PresenceProperties
    
    896
    -                                     ) -> None:
    
    897
    -        affiliation = helpers.get_uf_affiliation(user_contact.affiliation)
    
    898
    -        nick = user_contact.name
    
    992
    +    def _on_room_presence_error(self,
    
    993
    +                                _contact: GroupchatContact,
    
    994
    +                                _signal_name: str,
    
    995
    +                                event: events.MUCRoomPresenceError
    
    996
    +                                ) -> None:
    
    899 997
     
    
    900
    -        assert properties.muc_user is not None
    
    901
    -        reason = properties.muc_user.reason
    
    902
    -        reason = '' if reason is None else f': {reason}'
    
    998
    +        self._process_muc_room_presence_error(event)
    
    903 999
     
    
    904
    -        actor = properties.muc_user.actor
    
    905
    -        # Group Chat: You have been kicked by Alice
    
    906
    -        actor = '' if actor is None else _(' by {actor}').format(actor=actor)
    
    1000
    +    def _process_muc_room_presence_error(self,
    
    1001
    +                                         event: events.MUCRoomPresenceError
    
    1002
    +                                         ) -> None:
    
    907 1003
     
    
    908
    -        if properties.is_muc_self_presence:
    
    909
    -            message = _('** Your Affiliation has been set to '
    
    910
    -                        '{affiliation}{actor}{reason}').format(
    
    911
    -                            affiliation=affiliation,
    
    912
    -                            actor=actor,
    
    913
    -                            reason=reason)
    
    914
    -        else:
    
    915
    -            message = _('** Affiliation of {nick} has been set to '
    
    916
    -                        '{affiliation}{actor}{reason}').format(
    
    917
    -                            nick=nick,
    
    918
    -                            affiliation=affiliation,
    
    919
    -                            actor=actor,
    
    920
    -                            reason=reason)
    
    1004
    +        error_message = to_user_string(event.error)
    
    1005
    +        self.add_info_message(_('Error: %s') % error_message)
    
    921 1006
     
    
    922
    -        self.add_info_message(message)
    
    1007
    +    def _on_room_destroyed(self,
    
    1008
    +                           _contact: GroupchatContact,
    
    1009
    +                           _signal_name: str,
    
    1010
    +                           event: events.MUCRoomDestroyed
    
    1011
    +                           ) -> None:
    
    923 1012
     
    
    924
    -    def _on_user_role_changed(self,
    
    925
    -                              _contact: GroupchatContact,
    
    926
    -                              _signal_name: str,
    
    927
    -                              user_contact: GroupchatParticipant,
    
    928
    -                              properties: PresenceProperties
    
    929
    -                              ) -> None:
    
    930
    -        role = helpers.get_uf_role(user_contact.role)
    
    931
    -        nick = user_contact.name
    
    1013
    +        self._process_muc_room_destroyed(event)
    
    1014
    +
    
    1015
    +    def _process_muc_room_destroyed(self,
    
    1016
    +                                    event: events.MUCRoomDestroyed
    
    1017
    +                                    ) -> None:
    
    932 1018
     
    
    933
    -        assert properties.muc_user is not None
    
    934
    -        reason = properties.muc_user.reason
    
    1019
    +        reason = event.reason
    
    935 1020
             reason = '' if reason is None else f': {reason}'
    
    936 1021
     
    
    937
    -        actor = properties.muc_user.actor
    
    938
    -        # Group Chat: You have been kicked by Alice
    
    939
    -        actor = '' if actor is None else _(' by {actor}').format(actor=actor)
    
    1022
    +        message = _('Group chat has been destroyed')
    
    940 1023
     
    
    941
    -        if properties.is_muc_self_presence:
    
    942
    -            message = _('** Your Role has been set to '
    
    943
    -                        '{role}{actor}{reason}').format(role=role,
    
    944
    -                                                        actor=actor,
    
    945
    -                                                        reason=reason)
    
    946
    -        else:
    
    947
    -            message = _('** Role of {nick} has been set to '
    
    948
    -                        '{role}{actor}{reason}').format(nick=nick,
    
    949
    -                                                        role=role,
    
    950
    -                                                        actor=actor,
    
    951
    -                                                        reason=reason)
    
    1024
    +        if event.alternate is not None:
    
    1025
    +            message += '\n' + _('You can join this group chat instead: '
    
    1026
    +                                'xmpp:%s?join') % str(event.alternate)
    
    952 1027
     
    
    953
    -        self.add_info_message(message)
    
    1028
    +        self.add_info_message(message, event.timestamp)
    
    954 1029
     
    
    955
    -    def _on_room_kicked(self,
    
    1030
    +    def _on_user_joined(self,
    
    956 1031
                             _contact: GroupchatContact,
    
    957 1032
                             _signal_name: str,
    
    958
    -                        properties: MessageProperties
    
    1033
    +                        _user_contact: GroupchatParticipant,
    
    1034
    +                        event: events.MUCUserJoined
    
    959 1035
                             ) -> None:
    
    960
    -        status_codes = properties.muc_status_codes or []
    
    961 1036
     
    
    962
    -        assert properties.muc_user is not None
    
    963
    -        reason = properties.muc_user.reason
    
    964
    -        reason = '' if reason is None else f': {reason}'
    
    1037
    +        self._process_muc_user_joined(event)
    
    965 1038
     
    
    966
    -        actor = properties.muc_user.actor
    
    967
    -        # Group Chat: You have been kicked by Alice
    
    968
    -        actor = '' if actor is None else _(' by {actor}').format(actor=actor)
    
    1039
    +    def _process_muc_user_joined(self, event: events.MUCUserJoined) -> None:
    
    1040
    +        assert isinstance(self.contact, GroupchatContact)
    
    969 1041
     
    
    970
    -        # Group Chat: We have been removed from the room by Alice: reason
    
    971
    -        message = _('You have been removed from the group chat{actor}{reason}')
    
    1042
    +        if not event.is_self:
    
    1043
    +            if self.contact.is_joined:
    
    1044
    +                self.conversation_view.add_muc_user_joined(event)
    
    1045
    +            return
    
    972 1046
     
    
    973
    -        if StatusCode.REMOVED_ERROR in status_codes:
    
    974
    -            # Handle 333 before 307, some MUCs add both
    
    975
    -            # Group Chat: Server kicked us because of an server error
    
    976
    -            message = _('You have left due '
    
    977
    -                        'to an error{reason}').format(reason=reason)
    
    978
    -            self.add_info_message(message)
    
    1047
    +        status_codes = event.status_codes or []
    
    979 1048
     
    
    980
    -        elif StatusCode.REMOVED_KICKED in status_codes:
    
    981
    -            # Group Chat: We have been kicked by Alice: reason
    
    982
    -            message = _('You have been '
    
    983
    -                        'kicked{actor}{reason}').format(actor=actor,
    
    984
    -                                                        reason=reason)
    
    985
    -            self.add_info_message(message)
    
    1049
    +        message = None
    
    1050
    +        if not self.contact.is_joined:
    
    1051
    +            # We just joined the room
    
    1052
    +            message = _('You (%s) joined the group chat') % event.nick
    
    986 1053
     
    
    987
    -        elif StatusCode.REMOVED_BANNED in status_codes:
    
    988
    -            # Group Chat: We have been banned by Alice: reason
    
    989
    -            message = _('You have been '
    
    990
    -                        'banned{actor}{reason}').format(actor=actor,
    
    991
    -                                                        reason=reason)
    
    992
    -            self.add_info_message(message)
    
    1054
    +        if StatusCode.NON_ANONYMOUS in status_codes:
    
    1055
    +            message = _('Any participant is allowed to see your full '
    
    1056
    +                        'XMPP Address')
    
    993 1057
     
    
    994
    -        elif StatusCode.REMOVED_AFFILIATION_CHANGE in status_codes:
    
    995
    -            # Group Chat: We were removed because of an affiliation change
    
    996
    -            reason = _(': Affiliation changed')
    
    997
    -            message = message.format(actor=actor, reason=reason)
    
    998
    -            self.add_info_message(message)
    
    1058
    +        if StatusCode.CONFIG_ROOM_LOGGING in status_codes:
    
    1059
    +            message = _('Conversations are stored on the server')
    
    999 1060
     
    
    1000
    -        elif StatusCode.REMOVED_NONMEMBER_IN_MEMBERS_ONLY in status_codes:
    
    1001
    -            # Group Chat: Room configuration changed
    
    1002
    -            reason = _(': Group chat configuration changed to members-only')
    
    1003
    -            message = message.format(actor=actor, reason=reason)
    
    1004
    -            self.add_info_message(message)
    
    1061
    +        if StatusCode.NICKNAME_MODIFIED in status_codes:
    
    1062
    +            message = _('The server has assigned or modified your '
    
    1063
    +                        'nickname in this group chat')
    
    1005 1064
     
    
    1006
    -        elif StatusCode.REMOVED_SERVICE_SHUTDOWN in status_codes:
    
    1007
    -            # Group Chat: Kicked because of server shutdown
    
    1008
    -            reason = ': System shutdown'
    
    1009
    -            message = message.format(actor=actor, reason=reason)
    
    1010
    -            self.add_info_message(message)
    
    1065
    +        if message is not None:
    
    1066
    +            self.add_info_message(message, event.timestamp)
    
    1011 1067
     
    
    1012 1068
         def _on_user_left(self,
    
    1013 1069
                           _contact: GroupchatContact,
    
    1014 1070
                           _signal_name: str,
    
    1015
    -                      user_contact: GroupchatParticipant,
    
    1016
    -                      properties: MessageProperties
    
    1071
    +                      _user_contact: GroupchatParticipant,
    
    1072
    +                      event: events.MUCUserLeft
    
    1017 1073
                           ) -> None:
    
    1018
    -        status_codes = properties.muc_status_codes or []
    
    1019
    -        nick = user_contact.name
    
    1020 1074
     
    
    1021
    -        assert properties.muc_user is not None
    
    1022
    -        reason = properties.muc_user.reason
    
    1023
    -        reason = '' if reason is None else f': {reason}'
    
    1075
    +        self._process_muc_user_left(event)
    
    1024 1076
     
    
    1025
    -        actor = properties.muc_user.actor
    
    1026
    -        # Group Chat: You have been kicked by Alice
    
    1027
    -        actor = '' if actor is None else _(' by {actor}').format(actor=actor)
    
    1077
    +    def _process_muc_user_left(self, event: events.MUCUserLeft) -> None:
    
    1078
    +        if event.is_self:
    
    1079
    +            return
    
    1028 1080
     
    
    1029
    -        # Group Chat: We have been removed from the room
    
    1030
    -        message = _('{nick} has been removed from the group chat{by}{reason}')
    
    1081
    +        status_codes = event.status_codes or []
    
    1082
    +        nick = event.nick
    
    1031 1083
     
    
    1032 1084
             if StatusCode.REMOVED_ERROR in status_codes:
    
    1033 1085
                 # Handle 333 before 307, some MUCs add both
    
    1034
    -            self.conversation_view.add_muc_user_left(
    
    1035
    -                nick, properties.muc_user.reason, error=True)
    
    1086
    +            self.conversation_view.add_muc_user_left(event, error=True)
    
    1087
    +            return
    
    1036 1088
     
    
    1037
    -        elif StatusCode.REMOVED_KICKED in status_codes:
    
    1038
    -            # Group Chat: User was kicked by Alice: reason
    
    1089
    +        reason = event.reason
    
    1090
    +        reason = '' if reason is None else f': {reason}'
    
    1091
    +
    
    1092
    +        actor = event.actor
    
    1093
    +        # Group Chat: You have been kicked by Alice
    
    1094
    +        actor = '' if actor is None else _(' by {actor}').format(
    
    1095
    +            actor=actor)
    
    1096
    +
    
    1097
    +        message = _('{nick} has been removed from the group '
    
    1098
    +                    'chat{by}{reason}')
    
    1099
    +
    
    1100
    +        if StatusCode.REMOVED_KICKED in status_codes:
    
    1039 1101
                 message = _('{nick} has been '
    
    1040 1102
                             'kicked{actor}{reason}').format(nick=nick,
    
    1041 1103
                                                             actor=actor,
    
    1042 1104
                                                             reason=reason)
    
    1043
    -            self.add_info_message(message)
    
    1044 1105
     
    
    1045 1106
             elif StatusCode.REMOVED_BANNED in status_codes:
    
    1046
    -            # Group Chat: User was banned by Alice: reason
    
    1047 1107
                 message = _('{nick} has been '
    
    1048 1108
                             'banned{actor}{reason}').format(nick=nick,
    
    1049 1109
                                                             actor=actor,
    
    1050 1110
                                                             reason=reason)
    
    1051
    -            self.add_info_message(message)
    
    1052 1111
     
    
    1053 1112
             elif StatusCode.REMOVED_AFFILIATION_CHANGE in status_codes:
    
    1054 1113
                 reason = _(': Affiliation changed')
    
    1055 1114
                 message = message.format(nick=nick, by=actor, reason=reason)
    
    1056
    -            self.add_info_message(message)
    
    1057 1115
     
    
    1058 1116
             elif StatusCode.REMOVED_NONMEMBER_IN_MEMBERS_ONLY in status_codes:
    
    1059 1117
                 reason = _(': Group chat configuration changed to members-only')
    
    1060 1118
                 message = message.format(nick=nick, by=actor, reason=reason)
    
    1061
    -            self.add_info_message(message)
    
    1062 1119
     
    
    1063 1120
             else:
    
    1064
    -            self.conversation_view.add_muc_user_left(
    
    1065
    -                nick, properties.muc_user.reason)
    
    1121
    +            self.conversation_view.add_muc_user_left(event)
    
    1122
    +            return
    
    1066 1123
     
    
    1067
    -    def _on_room_presence_error(self,
    
    1068
    -                                _contact: GroupchatContact,
    
    1069
    -                                _signal_name: str,
    
    1070
    -                                properties: PresenceProperties
    
    1071
    -                                ) -> None:
    
    1124
    +        self.add_info_message(message, event.timestamp)
    
    1072 1125
     
    
    1073
    -        assert properties.error is not None
    
    1074
    -        error_message = to_user_string(properties.error)
    
    1075
    -        self.add_info_message(_('Error: %s') % error_message)
    
    1126
    +    def add_muc_message(self,
    
    1127
    +                        text: str,
    
    1128
    +                        tim: float,
    
    1129
    +                        contact: str = '',
    
    1130
    +                        displaymarking: Optional[Displaymarking] = None,
    
    1131
    +                        message_id: Optional[str] = None,
    
    1132
    +                        stanza_id: Optional[str] = None,
    
    1133
    +                        additional_data: Optional[AdditionalDataDict] = None,
    
    1134
    +                        ) -> None:
    
    1076 1135
     
    
    1077
    -    def _on_room_destroyed(self,
    
    1078
    -                           _contact: GroupchatContact,
    
    1079
    -                           _signal_name: str,
    
    1080
    -                           properties: PresenceProperties
    
    1081
    -                           ) -> None:
    
    1136
    +        assert isinstance(self._contact, GroupchatContact)
    
    1082 1137
     
    
    1083
    -        destroyed = properties.muc_destroyed
    
    1084
    -        assert destroyed is not None
    
    1138
    +        if contact == self._contact.nickname:
    
    1139
    +            kind = 'outgoing'
    
    1140
    +        else:
    
    1141
    +            kind = 'incoming'
    
    1142
    +            # muc-specific chatstate
    
    1085 1143
     
    
    1086
    -        reason = destroyed.reason
    
    1087
    -        reason = '' if reason is None else f': {reason}'
    
    1144
    +        self._add_message(text,
    
    1145
    +                          kind,
    
    1146
    +                          contact,
    
    1147
    +                          tim,
    
    1148
    +                          displaymarking=displaymarking,
    
    1149
    +                          message_id=message_id,
    
    1150
    +                          stanza_id=stanza_id,
    
    1151
    +                          additional_data=additional_data)
    
    1088 1152
     
    
    1089
    -        message = _('Group chat has been destroyed')
    
    1090
    -        self.add_info_message(message)
    
    1153
    +    def _on_room_subject(self,
    
    1154
    +                         contact: GroupchatContact,
    
    1155
    +                         _signal_name: str,
    
    1156
    +                         subject: Optional[MucSubject]
    
    1157
    +                         ) -> None:
    
    1091 1158
     
    
    1092
    -        alternate = destroyed.alternate
    
    1093
    -        if alternate is not None:
    
    1094
    -            join_message = _('You can join this group chat '
    
    1095
    -                             'instead: xmpp:%s?join') % str(alternate)
    
    1096
    -            self.add_info_message(join_message)
    1159
    +        if subject is None:
    
    1160
    +            return
    
    1161
    +
    
    1162
    +        _timestamp, old_subject = self._muc_subjects.get(contact, (None, None))
    
    1163
    +        if old_subject is not None and old_subject.text == subject.text:
    
    1164
    +            # Probably a rejoin, we already showed that subject
    
    1165
    +            return
    
    1166
    +
    
    1167
    +        self._muc_subjects[contact] = (time.time(), subject)
    
    1168
    +
    
    1169
    +        if (app.settings.get('show_subject_on_join') or
    
    1170
    +                not contact.is_joining):
    
    1171
    +            self.conversation_view.add_muc_subject(subject)
    
    1172
    +
    
    1173
    +    def rejoin(self) -> None:
    
    1174
    +        self.client.get_module('MUC').join(self.contact.jid)

  • gajim/gtk/conversation/rows/muc_join_left.py
    ... ... @@ -27,13 +27,19 @@
    27 27
     
    
    28 28
     
    
    29 29
     class MUCJoinLeft(BaseRow):
    
    30
    -    def __init__(self, type_: str, account: str, nick: str,
    
    30
    +    def __init__(self,
    
    31
    +                 type_: str,
    
    32
    +                 account: str,
    
    33
    +                 nick: str,
    
    31 34
                      reason: Optional[str] = None,
    
    32
    -                 error: bool = False):
    
    35
    +                 error: bool = False,
    
    36
    +                 timestamp: Optional[float] = None
    
    37
    +                 ) -> None:
    
    38
    +
    
    33 39
             BaseRow.__init__(self, account)
    
    34 40
     
    
    35 41
             self.type = type_
    
    36
    -        timestamp = time.time()
    
    42
    +        timestamp = timestamp or time.time()
    
    37 43
             self.timestamp = datetime.fromtimestamp(timestamp)
    
    38 44
             self.db_timestamp = timestamp
    
    39 45
     
    

  • gajim/gtk/conversation/view.py
    ... ... @@ -37,9 +37,7 @@
    37 37
     
    
    38 38
     from gajim.common import app
    
    39 39
     from gajim.common import types
    
    40
    -from gajim.common.events import JingleRequestReceived
    
    41
    -from gajim.common.events import FileRequestReceivedEvent
    
    42
    -from gajim.common.events import FileRequestSent
    
    40
    +from gajim.common import events
    
    43 41
     from gajim.common.helpers import AdditionalDataDict
    
    44 42
     from gajim.common.helpers import to_user_string
    
    45 43
     from gajim.common.helpers import get_start_of_day
    
    ... ... @@ -175,26 +173,29 @@ def add_muc_subject(self,
    175 173
             self._insert_message(muc_subject)
    
    176 174
     
    
    177 175
         def add_muc_user_left(self,
    
    178
    -                          nick: str,
    
    179
    -                          reason: str,
    
    180
    -                          error: bool = False) -> None:
    
    176
    +                          event: events.MUCUserLeft,
    
    177
    +                          error: bool = False
    
    178
    +                          ) -> None:
    
    179
    +
    
    181 180
             assert isinstance(self._contact, GroupchatContact)
    
    182 181
             if not self._contact.settings.get('print_join_left'):
    
    183 182
                 return
    
    184 183
             join_left = MUCJoinLeft('muc-user-left',
    
    185 184
                                     self._contact.account,
    
    186
    -                                nick,
    
    187
    -                                reason=reason,
    
    188
    -                                error=error)
    
    185
    +                                event.nick,
    
    186
    +                                reason=event.reason,
    
    187
    +                                error=error,
    
    188
    +                                timestamp=event.timestamp)
    
    189 189
             self._insert_message(join_left)
    
    190 190
     
    
    191
    -    def add_muc_user_joined(self, nick: str) -> None:
    
    191
    +    def add_muc_user_joined(self, event: events.MUCUserJoined) -> None:
    
    192 192
             assert isinstance(self._contact, GroupchatContact)
    
    193 193
             if not self._contact.settings.get('print_join_left'):
    
    194 194
                 return
    
    195 195
             join_left = MUCJoinLeft('muc-user-joined',
    
    196 196
                                     self._contact.account,
    
    197
    -                                nick)
    
    197
    +                                event.nick,
    
    198
    +                                timestamp=event.timestamp)
    
    198 199
             self._insert_message(join_left)
    
    199 200
     
    
    200 201
         def add_user_status(self, name: str, show: str, status: str) -> None:
    
    ... ... @@ -215,8 +216,8 @@ def add_file_transfer(self, transfer: HTTPFileTransfer) -> None:
    215 216
     
    
    216 217
         def add_jingle_file_transfer(self,
    
    217 218
                                      event: Union[
    
    218
    -                                     FileRequestReceivedEvent,
    
    219
    -                                     FileRequestSent,
    
    219
    +                                     events.FileRequestReceivedEvent,
    
    220
    +                                     events.FileRequestSent,
    
    220 221
                                          None] = None,
    
    221 222
                                      db_message: Optional[ConversationRow] = None
    
    222 223
                                      ) -> None:
    
    ... ... @@ -230,7 +231,7 @@ def add_jingle_file_transfer(self,
    230 231
             self._insert_message(jingle_transfer_row)
    
    231 232
     
    
    232 233
         def add_call_message(self,
    
    233
    -                         event: Optional[JingleRequestReceived] = None,
    
    234
    +                         event: Optional[events.JingleRequestReceived] = None,
    
    234 235
                              db_message: Optional[ConversationRow] = None
    
    235 236
                              ) -> None:
    
    236 237
             assert isinstance(self._contact, BareContact)
    

  • gajim/gtk/groupchat_nick_completion.py
    ... ... @@ -18,7 +18,6 @@
    18 18
     
    
    19 19
     import logging
    
    20 20
     
    
    21
    -from nbxmpp.structs import PresenceProperties
    
    22 21
     
    
    23 22
     from gi.repository import Gtk
    
    24 23
     from gi.repository import Gdk
    
    ... ... @@ -27,6 +26,7 @@
    27 26
     from gajim.common import ged
    
    28 27
     from gajim.common import types
    
    29 28
     from gajim.common.events import GcMessageReceived
    
    29
    +from gajim.common.events import MUCNicknameChanged
    
    30 30
     from gajim.common.ged import EventHelper
    
    31 31
     from gajim.common.helpers import jid_is_blocked
    
    32 32
     from gajim.common.helpers import message_needs_highlight
    
    ... ... @@ -76,12 +76,12 @@ def switch_contact(self, contact: types.GroupchatContactT) -> None:
    76 76
         def _on_user_nickname_changed(self,
    
    77 77
                                       _contact: types.GroupchatContact,
    
    78 78
                                       _signal_name: str,
    
    79
    +                                  event: MUCNicknameChanged,
    
    79 80
                                       old_contact: types.GroupchatParticipant,
    
    80
    -                                  new_contact: types.GroupchatParticipant,
    
    81
    -                                  properties: PresenceProperties
    
    81
    +                                  new_contact: types.GroupchatParticipant
    
    82 82
                                       ) -> None:
    
    83 83
     
    
    84
    -        if properties.is_muc_self_presence:
    
    84
    +        if event.is_self:
    
    85 85
                 return
    
    86 86
     
    
    87 87
             old_name = old_contact.name
    

  • gajim/gtk/groupchat_roster.py
    ... ... @@ -25,7 +25,6 @@
    25 25
     from gi.repository import Gtk
    
    26 26
     from gi.repository import GLib
    
    27 27
     from nbxmpp.const import Affiliation
    
    28
    -from nbxmpp.structs import PresenceProperties
    
    29 28
     
    
    30 29
     from gajim.common import app
    
    31 30
     from gajim.common import ged
    
    ... ... @@ -36,6 +35,7 @@
    36 35
     from gajim.common.const import AvatarSize
    
    37 36
     from gajim.common.const import StyleAttr
    
    38 37
     from gajim.common.events import ApplicationEvent
    
    38
    +from gajim.common.events import MUCNicknameChanged
    
    39 39
     from gajim.common.modules.contacts import GroupchatContact
    
    40 40
     
    
    41 41
     from .menus import get_groupchat_roster_menu
    
    ... ... @@ -281,11 +281,11 @@ def _update_contact(self,
    281 281
             self._add_contact(user_contact)
    
    282 282
     
    
    283 283
         def _on_user_nickname_changed(self,
    
    284
    -                                  contact: types.GroupchatContact,
    
    284
    +                                  _contact: types.GroupchatContact,
    
    285 285
                                       _signal_name: str,
    
    286
    +                                  _event: MUCNicknameChanged,
    
    286 287
                                       old_contact: types.GroupchatParticipant,
    
    287
    -                                  new_contact: types.GroupchatParticipant,
    
    288
    -                                  properties: PresenceProperties
    
    288
    +                                  new_contact: types.GroupchatParticipant
    
    289 289
                                       ) -> None:
    
    290 290
     
    
    291 291
             self._remove_contact(old_contact)
    

  • pyrightconfig.json
    ... ... @@ -38,7 +38,7 @@
    38 38
             "gajim/common/regex.py",
    
    39 39
             "gajim/common/setting_values.py",
    
    40 40
             "gajim/common/sound.py",
    
    41
    -        "gajim/common/storage/",
    
    41
    +        "gajim/common/storage/*",
    
    42 42
             "gajim/common/task_manager.py",
    
    43 43
             "gajim/gajim.py",
    
    44 44
             "gajim/gajim_remote.py",