home · contact · privacy
Properly parse MODE settings on users.
authorChristian Heller <c.heller@plomlompom.de>
Mon, 17 Nov 2025 22:42:47 +0000 (23:42 +0100)
committerChristian Heller <c.heller@plomlompom.de>
Mon, 17 Nov 2025 22:42:47 +0000 (23:42 +0100)
src/ircplom/client.py
src/tests/caps.test
src/tests/channels.test
src/tests/isupports.test
src/tests/lib/001-setting-nick
src/tests/lib/001-to-usermode
src/tests/lib/req-sasl
src/tests/lib/user-set-to
src/tests/lib/usermode [new file with mode: 0644]
src/tests/test.test
src/tests/userset.test

index c9c12d96f1ba7f420a4ccbbde1825a8036c44f7e..d9acd6aa4c2c3c6078aa072f5ba55ad3e5c334a7 100644 (file)
@@ -280,6 +280,7 @@ class _User(_SetNickuserhostMixin, User):
                  names_channels_of_user: Callable,
                  remove_from_channels: Callable,
                  **kwargs) -> None:
+        self._modes: set[str] = set()
         self.names_channels = lambda: names_channels_of_user(self)
         self._remove_from_channels = lambda target, msg: remove_from_channels(
                 self, target, msg)
@@ -303,6 +304,21 @@ class _User(_SetNickuserhostMixin, User):
     def id_(self, value: str) -> None:
         self._id_ = value
 
+    @property
+    def modes(self) -> str:
+        'User modes as collected by MODE messages on them.'
+        return ''.join(sorted(list(self._modes)))
+
+    @modes.setter
+    def modes(self, modeset: str) -> None:
+        operation, chars = modeset[:1], modeset[1:]
+        assert chars and operation in '+-'
+        for char in chars:
+            if operation == '+':
+                self._modes.add(char)
+            elif char in self._modes:
+                self._modes.remove(char)
+
 
 class _UpdatingServerCapability(UpdatingAttrsMixin, ServerCapability):
     pass
index a7ec02a54694073609aaf2a5b850e600e1b3f581..1c951d4bc54fc61a075b9ca9a2fc49212fd8ac71 100644 (file)
@@ -1,5 +1,4 @@
-insert ./lib/001-to-usermode
-# for: 001-to-usermode
+insert ./lib/001-setting-nick
 insert ./lib/attempting-to-connected
 insert ./lib/connect-to-connected
 insert ./lib/cap-msg
@@ -12,9 +11,11 @@ insert ./lib/disconnect
 insert ./lib/req-sasl
 # for: get-sasl-plain, req-sasl
 insert ./lib/servermsglogged
+insert ./lib/usermode
+insert ./lib/user-set-to
 
 × disconnect-to-reconnect-0
-insert 001-to-usermode
+insert usermode
 insert cmd-disconnect
 
 × disconnect-to-reconnect-1
@@ -34,6 +35,7 @@ insert connect-to-connected
 insert caps-neg-empty
 
 > /window 2
+insert 001-setting-nick
 insert disconnect-to-reconnect-0 :-1
 insert disconnect-to-reconnect-1
 
@@ -52,8 +54,7 @@ log 1 $ caps:zab:data set to: []
 log 1 $ caps:zba:data set to: []
 log 1 > CAP :LIST
 insert cap-msg : + CAPMSG :foo LIST * :foo rab oof
-log 1 $ users:me:nick set to: [?]
-log 1 $ users:me:nick set to: [foo]
+insert user-set-to :3 + USER_ID=me USERNICK :foo
 insert cap-msg : + CAPMSG :foo LIST :zab rba
 log 1 $ caps:foo:enabled set to: [True]
 log 1 $ caps:oof:enabled set to: [True]
@@ -62,7 +63,7 @@ log 1 $ caps:rba:enabled set to: [True]
 log 1 $ caps:zab:enabled set to: [True]
 log 1 > CAP :END
 
-insert disconnect-to-reconnect 4:
+insert disconnect-to-reconnect
 
 # check REQ for "sasl" (even if no PLAIN), denied
 insert servermsglogged : + MSG ::*.?.net CAP * LS :foo bar sasl
@@ -72,7 +73,7 @@ log 1 $ caps:sasl:data set to: []
 insert req-sasl : + REPLY=NAK CAPLIST :
 log 1 > CAP :END
 
-insert disconnect-to-reconnect 4:
+insert disconnect-to-reconnect
 
 # check REQ for "sasl" with PLAIN successful (but for lack of password, negotiation still ending instantly)
 insert get-sasl-plain
index 485ef911e1350f35ac446435dc746c289019912f..37460ddedeaa5cf80900e541569c95d7629e9455 100644 (file)
@@ -17,6 +17,7 @@ insert ./lib/privmsg
 insert ./lib/retry-in
 insert ./lib/servermsglogged
 insert ./lib/user-set-to
+insert ./lib/usermode
 
 × part-empty
 insert part : + USERIDS_CLEAR :cleared
@@ -98,7 +99,7 @@ insert part-empty : + CHAN_WIN_ID=3 CHANNEL :#ch_test0
 
 # check /join to, and part from, channel with one other user
 insert join-channel-0 : + CHANNEL=#ch_test0 RESIDENT_NAMES :foo bar
-insert user-set-to :2 + USER_ID=1 USERNICK :bar
+insert user-set-to :3 + USER_ID=1 USERNICK :bar
 insert join-channel-1 : + CHANNEL=#ch_test0 RESIDENT_IDS :[1], [me]
 log 3 $ residents: bar, foo
 insert part : + CHANNEL=#ch_test0 CHAN_WIN_ID=3 USERIDS_CLEAR :set to: [1]
@@ -106,11 +107,11 @@ log 1 $ users:1 cleared
 
 # check /join into channel with many other users, with multi-line 353
 insert join-channel-0 : + CHANNEL=#ch_test0 RESIDENT_NAMES :foo baz oof
-insert user-set-to :2 + USER_ID=2 USERNICK :baz
-insert user-set-to :2 + USER_ID=3 USERNICK :oof
+insert user-set-to :3 + USER_ID=2 USERNICK :baz
+insert user-set-to :3 + USER_ID=3 USERNICK :oof
 insert servermsglogged : + MSG ::foo.bar.baz 353 foo = #ch_test0 :rab zab
-insert user-set-to :2 + USER_ID=4 USERNICK :rab
-insert user-set-to :2 + USER_ID=5 USERNICK :zab
+insert user-set-to :3 + USER_ID=4 USERNICK :rab
+insert user-set-to :3 + USER_ID=5 USERNICK :zab
 insert join-channel-1 : + CHANNEL=#ch_test0 RESIDENT_IDS :[2], [3], [4], [5], [me]
 log 3 $ residents: baz, oof, rab, zab, foo
 
@@ -128,7 +129,7 @@ log 4 $ residents: baz, foo
 
 # check _observed_ topic change _does_ affect users database, and …
 insert servermsglogged : + MSG ::baz!~baz@baz.baz TOPIC #ch_test1 :foo bar baz
-insert user-set-to 2: + USER_ID=2 USERNAME=~baz USERHOST :baz.baz
+insert user-set-to 3: + USER_ID=2 USERNAME=~baz USERHOST :baz.baz
 
 # … is echoed into channel window _if_ either content or setter change
 insert topic-set-to : + CHANNEL=#ch_test1 NEWTOPIC :foo bar baz
@@ -140,7 +141,7 @@ insert topic-set-to : + CHANNEL=#ch_test1 NEWTOPIC :foo foo baz
 insert servermsglogged : + MSG ::baz!~baz@baz.baz PRIVMSG #ch_test0 :msg_test3 msg_test4
 log 3 < [baz] msg_test3 msg_test4
 insert servermsglogged : + MSG ::oof!~oof@oof.oof NOTICE #ch_test0 :msg_test5 msg_test6
-insert user-set-to 2: + USER_ID=3 USERNAME=~oof USERHOST :oof.oof
+insert user-set-to 3: + USER_ID=3 USERNAME=~oof USERHOST :oof.oof
 log 3 < (oof) msg_test5 msg_test6
 
 # check effect of server NOTICE to channel
@@ -154,7 +155,7 @@ log 1 $ users:3 cleared
 
 # check other-user part with exit message
 insert servermsglogged : + MSG ::zab!~zab@zab.zab PART #ch_test0 :goodbye
-insert user-set-to 2: + USER_ID=5 USERNAME=~zab USERHOST :zab.zab
+insert user-set-to 3: + USER_ID=5 USERNAME=~zab USERHOST :zab.zab
 insert parts-core : + CHAN_WIN_ID=3 CHANNEL=#ch_test0 USER_ID=5 NICK=zab exitPREFIX=:§ exitMSG=goodbye USERIDS_CLEAR=set§to:§[4],§[me] § : 
 log 1 $ users:5 cleared
 
index 6d147e0cd1349302f94ed24e0736c1b1841d2dd7..7cddea251e2bf2355a99600fed5c2a53f03e40b0 100644 (file)
@@ -83,15 +83,15 @@ insert join-empty : + CHAN_WIN_ID=6 CHANNEL :#ch_test5
 
 # test effect of PREFIX
 insert join-channel-0 : +0 CHANNEL=#ch_test6 RESIDENT_NAMES :foo @bar +baz =quux
-insert user-set-to :2 +1 USER_ID=1 USERNICK :bar
-insert user-set-to :2 +1 USER_ID=2 USERNICK :baz
-insert user-set-to :2 +1 USER_ID=3 USERNICK :=quux
+insert user-set-to :3 +1 USER_ID=1 USERNICK :bar
+insert user-set-to :3 +1 USER_ID=2 USERNICK :baz
+insert user-set-to :3 +1 USER_ID=3 USERNICK :=quux
 insert join-channel-1 : +0 CHANNEL=#ch_test6 RESIDENT_IDS :[1], [2], [3], [me]
 log 7 $ residents: bar, baz, =quux, foo
 insert un-default : +0 KEY=PREFIX VALUE :(vE)+=
 insert join-channel-0 : +0 CHANNEL=#ch_test7 RESIDENT_NAMES :foo @bar +baz =quux
-insert user-set-to :2 +1 USER_ID=4 USERNICK :@bar
-insert user-set-to :2 +1 USER_ID=5 USERNICK :quux
+insert user-set-to :3 +1 USER_ID=4 USERNICK :@bar
+insert user-set-to :3 +1 USER_ID=5 USERNICK :quux
 insert join-channel-1 : +0 CHANNEL=#ch_test7 RESIDENT_IDS :[2], [4], [5], [me]
 log 8 $ residents: baz, @bar, quux, foo
 
index 789db765bbe415ce219e086f9f9011a36df0958e..d01f3548c1c81bcd70e4623a22f349c23542089a 100644 (file)
@@ -3,4 +3,4 @@ insert ./lib/user-set-to
 
 × 001-setting-nick
 insert servermsglogged : +0 MSG ::foo.bar.baz 001 foo :Welcome to the foo.bar.baz network
-insert user-set-to :2 +1 USER_ID=me USERNICK :foo
+insert user-set-to :3 +1 USER_ID=me USERNICK :foo
index 2ab63eb9ac3517fcef29d20e4e414cb5a7b1fb01..60ef5ca780b0827e075b5788d1868addc4c7b06a 100644 (file)
@@ -1,11 +1,5 @@
 insert ./lib/001-setting-nick
-insert ./lib/servermsglogged
-insert ./lib/user-set-to
-
-× usermode
-insert servermsglogged : +0 MSG ::foo!~baz@baz.bar.foo MODE foo ABC
-insert user-set-to 2: +1 USER_ID=me USERNAME=~baz USERHOST :baz.bar.foo
-log 1 $ users:me:modes set to: [ABC]
+insert ./lib/usermode
 
 × 001-to-usermode
 insert 001-setting-nick
index 49c07c1bcb64aec817d05cafb6307f82541799cc..55b132496f4a8cb12cd68cdccbb988a34892efa1 100644 (file)
@@ -6,7 +6,7 @@ insert ./lib/user-set-to
 log 1 > CAP REQ :sasl
 log 1 > CAP :LIST
 insert cap-msg : +0 CAPMSG :foo REPLY :sasl
-insert user-set-to :2 + USER_ID=me USERNICK :foo
+insert user-set-to :3 + USER_ID=me USERNICK :foo
 insert cap-msg : +0 CAPMSG :foo LIST :CAPLIST
 
 × get-sasl-plain
index b41abb52602acd1e75d4916744fcd0b29e970aa1..da5bf50fe782cbd2d5fb0c1361786ae7f974e02d 100644 (file)
@@ -1,5 +1,6 @@
 × user-set-to
 log 1 $ users:USER_ID:nick set to: [?]
+log 1 $ users:USER_ID:modes set to: []
 log 1 $ users:USER_ID:nick set to: [USERNICK]
 log 1 $ users:USER_ID:user set to: [USERNAME]
 log 1 $ users:USER_ID:host set to: [USERHOST]
diff --git a/src/tests/lib/usermode b/src/tests/lib/usermode
new file mode 100644 (file)
index 0000000..41755d0
--- /dev/null
@@ -0,0 +1,7 @@
+insert ./lib/servermsglogged
+insert ./lib/user-set-to
+
+× usermode
+insert servermsglogged : +0 MSG ::foo!~baz@baz.bar.foo MODE foo +ABC
+insert user-set-to 3: +1 USER_ID=me USERNAME=~baz USERHOST :baz.bar.foo
+log 1 $ users:me:modes set to: [ABC]
index 525d724f4587a6a7590f0e62ec511ca81ff7e069..4e3a9fdd1976e041b730b0580035c8191bc3dcde 100644 (file)
@@ -90,7 +90,7 @@ log 2 $ - (to this server) -
 
 # collect user mode
 insert servermsglogged : +0 MSG ::foo1 MODE foo1 :+Ziw
-log 1 $ users:me:modes set to: [+Ziw]
+log 1 $ users:me:modes set to: [Ziw]
 # handle bot query NOTICE
 insert servermsglogged : +0 MSG ::SaslServ!SaslServ@services.bar.baz NOTICE foo1 :Last login from ~baz@foo.bar.baz on Jan 1 22:00:00 2021 +0000.
 log 3 < (SaslServ) Last login from ~baz@foo.bar.baz on Jan 1 22:00:00 2021 +0000.
@@ -113,7 +113,7 @@ log rename_win_ids $ foo1!~baz@baz.bar.foo renames foo
 
 # join channel with other user
 insert join-channel-0 : +0 CHANNEL=#test RESIDENT_NAMES :foo @baz
-insert user-set-to :2 +1 USER_ID=1 USERNICK :baz
+insert user-set-to :3 +1 USER_ID=1 USERNICK :baz
 insert join-channel-1 : +0 CHANNEL=#test RESIDENT_IDS :[1], [me]
 log 4 $ residents: baz, foo
 
index ce8dcb4a230f0eddc3121968c965c6678e39a5bf..fe3b63dd2c4a6eda0f691fd7b12adbb82453f8e2 100644 (file)
@@ -1,13 +1,13 @@
-insert ./lib/conn
-# for: attempting, connected
+insert ./lib/cap-msg
 insert ./lib/cmd-disconnect
 # for cmd-disconnect-0, cmd-disconnect-1
+insert ./lib/conn
+# for: attempting, connected
 insert ./lib/disconnect
 # for: disconnect1
-insert ./lib/servermsglogged
-insert ./lib/cap-msg
 insert ./lib/req-sasl
 # for: get-sasl-plain
+insert ./lib/servermsglogged
 insert ./lib/user-set-to
 
 × connecting-to-nick
@@ -57,41 +57,39 @@ insert connecting-to-nick
 insert cap-msg : +0 CAPMSG :* LS :
 log 1 > CAP :LIST
 insert cap-msg : +0 CAPMSG :foo LIST :
-insert user-set-to :2 + USER_ID=me USERNICK :foo
+insert user-set-to :3 + USER_ID=me USERNICK :foo
 log 1 > CAP :END
 #
 insert cmd-disconnect-0 :-2 + ~baz@baz.bar.foo :~foo@foo.foo
-insert user-set-to 2: + USER_ID=me USERNAME=~foo USERHOST :foo.foo
+insert user-set-to 3: + USER_ID=me USERNAME=~foo USERHOST :foo.foo
 insert cmd-disconnect-0 -2: + ~baz@baz.bar.foo :~foo@foo.foo
 insert cmd-disconnect-1 :-1
 insert disconnect1 1: + WIN_IDS :2
 
-# check successful SASL authentication
+# check failing SASL authentication
 insert connecting-to-nick
 insert cap-to-auth
-insert servermsglogged : + MSG ::foo.bar.baz 900 foo foo!foo@foo.foo foo :You are now logged in as foo
-insert user-set-to 2: + USER_ID=me USERNAME=foo USERHOST :foo.foo
-log 1 $ sasl_account set to: [foo]
-insert end-auth : + NUMERIC=903 SASL_STATE :successful
+insert end-auth : + NUMERIC=904 SASL_STATE :failed
 #
 insert cmd-disconnect-0 :-2 + ~baz@baz.bar.foo :~foo@foo.foo
-insert user-set-to 2:3 + USER_ID=me USERNAME :~foo
+insert user-set-to 3: + USER_ID=me USERNAME=~foo USERHOST :foo.foo
 insert cmd-disconnect-0 -2: + ~baz@baz.bar.foo :~foo@foo.foo
 insert cmd-disconnect-1
 insert disconnect1 1:-1 + WIN_IDS :2
-log 1 $ sasl_account set to: []
 log 1 $ sasl_auth_state set to: []
 insert disconnect1 -1:
 
-# check failing SASL authentication
+# check successful SASL authentication
 insert connecting-to-nick
 insert cap-to-auth
-insert end-auth : + NUMERIC=904 SASL_STATE :failed
-#
-insert cmd-disconnect-0 :-2 + ~baz@baz.bar.foo :~foo@foo.foo
-insert user-set-to 2: + USER_ID=me USERNAME=~foo USERHOST :foo.foo
-insert cmd-disconnect-0 -2: + ~baz@baz.bar.foo :~foo@foo.foo
-insert cmd-disconnect-1
-insert disconnect1 1:-1 + WIN_IDS :2
-log 1 $ sasl_auth_state set to: []
-insert disconnect1 -1:
+insert servermsglogged : + MSG ::foo.bar.baz 900 foo foo!foo@foo.foo foo :You are now logged in as foo
+insert user-set-to 3: + USER_ID=me USERNAME=foo USERHOST :foo.foo
+log 1 $ sasl_account set to: [foo]
+insert end-auth : + NUMERIC=903 SASL_STATE :successful
+
+# check tilde addition and MODE setting
+insert servermsglogged : +0 MSG ::foo!~foo@foo.foo MODE foo +aBc 
+log 1 $ users:me:user set to: [~foo]
+log 1 $ users:me:modes set to: [Bac]
+insert servermsglogged : +0 MSG ::foo!~foo@foo.foo MODE foo -cba
+log 1 $ users:me:modes set to: [B]