66"""
77import gc
88import json
9- import weakref
10- from collections import Mapping , Iterable
9+ from collections import Mapping , Iterable , namedtuple
1110from contextlib import suppress
1211from numbers import Number
1312from operator import attrgetter
@@ -269,6 +268,15 @@ def get_chan_data(bot):
269268 update_conn_data (conn )
270269
271270
271+ def sort_member_status (member ):
272+ """
273+ :type member: Channel.Member
274+ """
275+ status = list (set (member .status ))
276+ status .sort (key = attrgetter ("level" ), reverse = True )
277+ member .status = status
278+
279+
272280def clean_user_data (user ):
273281 """
274282 :type user: User
@@ -543,6 +551,35 @@ def on_join(nick, user, host, conn, irc_paramlist):
543551 user_data .join_channel (chan_data )
544552
545553
554+ ModeChange = namedtuple ('ModeChange' , 'mode adding param is_status' )
555+
556+
557+ def _parse_mode_string (modes , params , status_modes , mode_types ):
558+ new_modes = []
559+ adding = True
560+ for c in modes :
561+ if c == '+' :
562+ adding = True
563+ elif c == '-' :
564+ adding = False
565+ else :
566+ is_status = c in status_modes
567+ mode_type = mode_types .get (c )
568+ if mode_type :
569+ mode_type = mode_type .type
570+ else :
571+ mode_type = 'B' if is_status else None
572+
573+ if mode_type in "AB" or (mode_type == 'C' and adding ):
574+ param = params .pop (0 )
575+ else :
576+ param = None
577+
578+ new_modes .append (ModeChange (c , adding , param , is_status ))
579+
580+ return new_modes
581+
582+
546583@hook .irc_raw ('MODE' )
547584def on_mode (chan , irc_paramlist , conn ):
548585 """
@@ -565,42 +602,31 @@ def on_mode(chan, irc_paramlist, conn):
565602 chan_data = get_chans (conn ).getchan (chan )
566603
567604 modes = irc_paramlist [1 ]
568- mode_params = irc_paramlist [2 :]
569- new_modes = {}
570- adding = True
571- for c in modes :
572- if c == '+' :
573- adding = True
574- elif c == '-' :
575- adding = False
605+ mode_params = list (irc_paramlist [2 :]).copy ()
606+ new_modes = _parse_mode_string (modes , mode_params , status_modes , mode_types )
607+ new_statuses = [change for change in new_modes if change .is_status ]
608+ to_sort = {}
609+ for change in new_statuses :
610+ status_char = change .mode
611+ nick = change .param
612+ user = get_users (conn ).getuser (nick )
613+ memb = chan_data .get_member (user , create = True )
614+ status = statuses [status_char ]
615+ memb_status = memb .status
616+ if change .adding :
617+ memb_status .append (status )
618+ to_sort [user .nick ] = memb
576619 else :
577- new_modes [c ] = adding
578- is_status = c in status_modes
579- mode_type = mode_types .get (c )
580- if mode_type :
581- mode_type = mode_type .type
620+ if status in memb_status :
621+ memb_status .remove (status )
582622 else :
583- mode_type = 'B' if is_status else None
623+ logger .debug (
624+ "[%s|chantrack] Attempt to remove status %s from user %s in channel %s" ,
625+ conn .name , status , user ['nick' ], chan
626+ )
584627
585- if mode_type in "AB" or (mode_type == 'C' and adding ):
586- param = mode_params .pop (0 )
587-
588- if is_status :
589- user = get_users (conn ).getuser (param )
590- memb = chan_data .get_member (user , create = True )
591- status = statuses [c ]
592- memb_status = memb .status
593- if adding :
594- memb_status .append (status )
595- memb_status .sort (key = attrgetter ("level" ), reverse = True )
596- else :
597- if status in memb_status :
598- memb_status .remove (status )
599- else :
600- logger .debug (
601- "[%s|chantrack] Attempt to remove status %s from user %s in channel %s" ,
602- conn .name , status , user ['nick' ], chan
603- )
628+ for member in to_sort .values ():
629+ sort_member_status (member )
604630
605631
606632@hook .irc_raw ('PART' )
0 commit comments