server: Add a maximum value for the number of nicklist users.
This commit is contained in:
parent
d896962b19
commit
74efe8ef9b
5 changed files with 122 additions and 35 deletions
11
main.py
11
main.py
|
@ -34,9 +34,14 @@ from logbook import Logger, StreamHandler
|
||||||
from nio import RemoteProtocolError, RemoteTransportError, TransportType
|
from nio import RemoteProtocolError, RemoteTransportError, TransportType
|
||||||
|
|
||||||
from matrix import globals as G
|
from matrix import globals as G
|
||||||
from matrix.bar_items import (init_bar_items, matrix_bar_item_buffer_modes,
|
from matrix.bar_items import (
|
||||||
matrix_bar_item_lag, matrix_bar_item_name,
|
init_bar_items,
|
||||||
matrix_bar_item_plugin)
|
matrix_bar_item_buffer_modes,
|
||||||
|
matrix_bar_item_lag,
|
||||||
|
matrix_bar_item_name,
|
||||||
|
matrix_bar_item_plugin,
|
||||||
|
matrix_bar_nicklist_count
|
||||||
|
)
|
||||||
from matrix.buffer import room_buffer_close_cb, room_buffer_input_cb
|
from matrix.buffer import room_buffer_close_cb, room_buffer_input_cb
|
||||||
# Weechat searches for the registered callbacks in the scope of the main script
|
# Weechat searches for the registered callbacks in the scope of the main script
|
||||||
# file, import the callbacks here so weechat can find them.
|
# file, import the callbacks here so weechat can find them.
|
||||||
|
|
|
@ -117,9 +117,26 @@ def matrix_bar_item_buffer_modes(data, item, window, buffer, extra_info):
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
@utf8_decode
|
||||||
|
def matrix_bar_nicklist_count(data, item, window, buffer, extra_info):
|
||||||
|
# pylint: disable=unused-argument
|
||||||
|
for server in SERVERS.values():
|
||||||
|
if buffer in server.buffers.values():
|
||||||
|
room_buffer = server.find_room_from_ptr(buffer)
|
||||||
|
room = room_buffer.room
|
||||||
|
return str(len(room.users))
|
||||||
|
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
def init_bar_items():
|
def init_bar_items():
|
||||||
W.bar_item_new("(extra)buffer_plugin", "matrix_bar_item_plugin", "")
|
W.bar_item_new("(extra)buffer_plugin", "matrix_bar_item_plugin", "")
|
||||||
W.bar_item_new("(extra)buffer_name", "matrix_bar_item_name", "")
|
W.bar_item_new("(extra)buffer_name", "matrix_bar_item_name", "")
|
||||||
W.bar_item_new("(extra)lag", "matrix_bar_item_lag", "")
|
W.bar_item_new("(extra)lag", "matrix_bar_item_lag", "")
|
||||||
|
W.bar_item_new(
|
||||||
|
"(extra)buffer_nicklist_count",
|
||||||
|
"matrix_bar_nicklist_count",
|
||||||
|
""
|
||||||
|
)
|
||||||
W.bar_item_new("(extra)buffer_modes", "matrix_bar_item_buffer_modes", "")
|
W.bar_item_new("(extra)buffer_modes", "matrix_bar_item_buffer_modes", "")
|
||||||
W.bar_item_new("(extra)matrix_modes", "matrix_bar_item_buffer_modes", "")
|
W.bar_item_new("(extra)matrix_modes", "matrix_bar_item_buffer_modes", "")
|
||||||
|
|
|
@ -692,7 +692,7 @@ class WeechatChannelBuffer(object):
|
||||||
message = self._membership_message(user, "invite")
|
message = self._membership_message(user, "invite")
|
||||||
self.print_date_tags(message, date, tags + (extra_tags or []))
|
self.print_date_tags(message, date, tags + (extra_tags or []))
|
||||||
|
|
||||||
def _remove_user_from_nicklist(self, user):
|
def remove_user_from_nicklist(self, user):
|
||||||
# type: (WeechatUser) -> None
|
# type: (WeechatUser) -> None
|
||||||
nick_pointer = W.nicklist_search_nick(self._ptr, "", user.nick)
|
nick_pointer = W.nicklist_search_nick(self._ptr, "", user.nick)
|
||||||
|
|
||||||
|
@ -702,7 +702,7 @@ class WeechatChannelBuffer(object):
|
||||||
def _leave(self, nick, date, message, leave_type, extra_tags=None):
|
def _leave(self, nick, date, message, leave_type, extra_tags=None):
|
||||||
# type: (str, int, bool, str, List[str]) -> None
|
# type: (str, int, bool, str, List[str]) -> None
|
||||||
user = self._get_user(nick)
|
user = self._get_user(nick)
|
||||||
self._remove_user_from_nicklist(user)
|
self.remove_user_from_nicklist(user)
|
||||||
|
|
||||||
if len(self.users) <= 2:
|
if len(self.users) <= 2:
|
||||||
W.buffer_set(self._ptr, "localvar_set_type", "private")
|
W.buffer_set(self._ptr, "localvar_set_type", "private")
|
||||||
|
@ -807,6 +807,7 @@ class RoomBuffer(object):
|
||||||
self.leave_event_id = None # type: Optional[str]
|
self.leave_event_id = None # type: Optional[str]
|
||||||
self.members_fetched = False
|
self.members_fetched = False
|
||||||
self.unhandled_users = [] # type: List[str]
|
self.unhandled_users = [] # type: List[str]
|
||||||
|
self.inactive_users = []
|
||||||
|
|
||||||
buffer_name = "{}.{}".format(server_name, room.room_id)
|
buffer_name = "{}.{}".format(server_name, room.room_id)
|
||||||
|
|
||||||
|
@ -840,7 +841,11 @@ class RoomBuffer(object):
|
||||||
|
|
||||||
return user_id
|
return user_id
|
||||||
|
|
||||||
def add_user(self, user_id, date, is_state):
|
def add_user(self, user_id, date, is_state, force_add=False):
|
||||||
|
# User is already added don't add him again.
|
||||||
|
if user_id in self.displayed_nicks:
|
||||||
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
user = self.room.users[user_id]
|
user = self.room.users[user_id]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
@ -848,10 +853,25 @@ class RoomBuffer(object):
|
||||||
# yet to come, so do nothing
|
# yet to come, so do nothing
|
||||||
return
|
return
|
||||||
|
|
||||||
# User is already added don't add him again.
|
# Adding users to the nicklist is a O(1) + search time
|
||||||
if user_id in self.displayed_nicks:
|
# operation (the nicks are added to a linked list sorted).
|
||||||
|
# The search time is O(N * min(a,b)) where N is the number
|
||||||
|
# of nicks already added and a/b are the length of
|
||||||
|
# the strings that are compared at every itteration.
|
||||||
|
# Because the search time get's increasingly longer we're
|
||||||
|
# going to stop adding inactive users, they will be lazily added if
|
||||||
|
# they become active.
|
||||||
|
if is_state and not force_add and user.power_level <= 0:
|
||||||
|
if (len(self.displayed_nicks) >=
|
||||||
|
G.CONFIG.network.max_nicklist_users):
|
||||||
|
self.inactive_users.append(user_id)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.inactive_users.remove(user_id)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
short_name = shorten_sender(user.user_id)
|
short_name = shorten_sender(user.user_id)
|
||||||
|
|
||||||
# TODO handle this special case for discord bridge users and
|
# TODO handle this special case for discord bridge users and
|
||||||
|
@ -882,27 +902,12 @@ class RoomBuffer(object):
|
||||||
date = server_ts_to_weechat(event.server_timestamp)
|
date = server_ts_to_weechat(event.server_timestamp)
|
||||||
|
|
||||||
if event.content["membership"] == "join":
|
if event.content["membership"] == "join":
|
||||||
if event.state_key not in self.displayed_nicks:
|
if (event.state_key not in self.displayed_nicks
|
||||||
# Adding users to the nicklist is a O(1) + search time
|
and event.state_key not in self.inactive_users):
|
||||||
# operation (the nicks are added to a linked list sorted).
|
if len(self.room.users) > 100:
|
||||||
# The search time is O(N * min(a,b)) where N is the number
|
|
||||||
# of nicks already added and a/b are the length of
|
|
||||||
# the strings that are compared at every itteration.
|
|
||||||
# Because the search time get's increasingly longer we're
|
|
||||||
# going to add nicks later in a timer hook.
|
|
||||||
if (len(self.displayed_nicks) > 100
|
|
||||||
and is_state):
|
|
||||||
# Always add users with a high power level
|
|
||||||
try:
|
|
||||||
user = self.room.users[event.state_key]
|
|
||||||
except KeyError:
|
|
||||||
self.unhandled_users.append(event.state_key)
|
self.unhandled_users.append(event.state_key)
|
||||||
else:
|
return
|
||||||
if user.power_level > 0:
|
|
||||||
self.add_user(event.state_key, date, is_state)
|
|
||||||
else:
|
|
||||||
self.unhandled_users.append(event.state_key)
|
|
||||||
else:
|
|
||||||
self.add_user(event.state_key, date, is_state)
|
self.add_user(event.state_key, date, is_state)
|
||||||
else:
|
else:
|
||||||
# TODO print out profile changes
|
# TODO print out profile changes
|
||||||
|
@ -1062,7 +1067,7 @@ class RoomBuffer(object):
|
||||||
|
|
||||||
# There is no way to change the group of a user without
|
# There is no way to change the group of a user without
|
||||||
# removing him from the nicklist
|
# removing him from the nicklist
|
||||||
self.weechat_buffer._remove_user_from_nicklist(user)
|
self.weechat_buffer.remove_user_from_nicklist(user)
|
||||||
self.weechat_buffer._add_user_to_nicklist(user)
|
self.weechat_buffer._add_user_to_nicklist(user)
|
||||||
|
|
||||||
def handle_state_event(self, event):
|
def handle_state_event(self, event):
|
||||||
|
@ -1087,7 +1092,7 @@ class RoomBuffer(object):
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
self.add_user(event.sender, 0, True)
|
self.add_user(event.sender, 0, True, True)
|
||||||
|
|
||||||
if isinstance(event, RoomMemberEvent):
|
if isinstance(event, RoomMemberEvent):
|
||||||
self.handle_membership_events(event, False)
|
self.handle_membership_events(event, False)
|
||||||
|
|
|
@ -396,6 +396,18 @@ class MatrixConfig(WeechatConfig):
|
||||||
"they will be loaded when the buffer becomes active. This "
|
"they will be loaded when the buffer becomes active. This "
|
||||||
"only affects non encrypted rooms."),
|
"only affects non encrypted rooms."),
|
||||||
),
|
),
|
||||||
|
Option(
|
||||||
|
"max_nicklist_users",
|
||||||
|
"integer",
|
||||||
|
"",
|
||||||
|
100,
|
||||||
|
20000,
|
||||||
|
"5000",
|
||||||
|
("Limit the number of users that are added to the nicklist. "
|
||||||
|
"Active users and users with a higher power level are always."
|
||||||
|
" Inactive users will be removed from the nicklist after a "
|
||||||
|
"day of inactivity."),
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
color_options = [
|
color_options = [
|
||||||
|
|
|
@ -252,6 +252,7 @@ class MatrixServer(object):
|
||||||
# type: DefaultDict[str, Deque[EncrytpionQueueItem]]
|
# type: DefaultDict[str, Deque[EncrytpionQueueItem]]
|
||||||
self.backlog_queue = dict() # type: Dict[str, str]
|
self.backlog_queue = dict() # type: Dict[str, str]
|
||||||
|
|
||||||
|
self.user_gc_time = time.time() # type: float
|
||||||
self.member_request_list = [] # type: List[str]
|
self.member_request_list = [] # type: List[str]
|
||||||
self.rooms_with_missing_members = [] # type: List[str]
|
self.rooms_with_missing_members = [] # type: List[str]
|
||||||
self.lazy_load_hook = None # type: Optional[str]
|
self.lazy_load_hook = None # type: Optional[str]
|
||||||
|
@ -1101,6 +1102,53 @@ class MatrixServer(object):
|
||||||
room_buffer = self.room_buffers[room_id]
|
room_buffer = self.room_buffers[room_id]
|
||||||
return room_buffer
|
return room_buffer
|
||||||
|
|
||||||
|
def garbage_collect_users(self):
|
||||||
|
""" Remove inactive users.
|
||||||
|
This tries to keep the number of users added to the nicklist less than
|
||||||
|
the configuration option matrix.network.max_nicklist_users. It
|
||||||
|
removes users that have not been active for a day until there are
|
||||||
|
less than max_nicklist_users or no users are left for removal.
|
||||||
|
It never removes users that have a bigger power level than the
|
||||||
|
default one.
|
||||||
|
This function is run every hour by the server timer callback"""
|
||||||
|
|
||||||
|
now = time.time()
|
||||||
|
self.user_gc_time = now
|
||||||
|
|
||||||
|
def day_passed(t1, t2):
|
||||||
|
return (t2 - t1) > 86400
|
||||||
|
|
||||||
|
for room_buffer in self.room_buffers.values():
|
||||||
|
to_remove = max(
|
||||||
|
(len(room_buffer.displayed_nicks) -
|
||||||
|
G.CONFIG.network.max_nicklist_users),
|
||||||
|
0
|
||||||
|
)
|
||||||
|
|
||||||
|
if not to_remove:
|
||||||
|
continue
|
||||||
|
|
||||||
|
removed = 0
|
||||||
|
removed_user_ids = []
|
||||||
|
|
||||||
|
for user_id, nick in room_buffer.displayed_nicks.items():
|
||||||
|
user = room_buffer.weechat_buffer.users[nick]
|
||||||
|
|
||||||
|
if (not user.speaking_time or
|
||||||
|
day_passed(user.speaking_time, now)):
|
||||||
|
room_buffer.weechat_buffer.part(nick, 0, False)
|
||||||
|
removed_user_ids.append(user_id)
|
||||||
|
removed += 1
|
||||||
|
|
||||||
|
if removed >= to_remove:
|
||||||
|
break
|
||||||
|
|
||||||
|
for user_id in removed_user_ids:
|
||||||
|
W.prnt("", "Garbage collected {}".format(user_id))
|
||||||
|
del room_buffer.displayed_nicks[user_id]
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
def buffer_merge(self):
|
def buffer_merge(self):
|
||||||
if not self.server_buffer:
|
if not self.server_buffer:
|
||||||
return
|
return
|
||||||
|
@ -1267,8 +1315,8 @@ def matrix_timer_cb(server_name, remaining_calls):
|
||||||
}
|
}
|
||||||
server.sync(timeout, sync_filter)
|
server.sync(timeout, sync_filter)
|
||||||
|
|
||||||
if not server.next_batch:
|
if current_time > (server.user_gc_time + 3600):
|
||||||
return W.WEECHAT_RC_OK
|
server.garbage_collect_users()
|
||||||
|
|
||||||
return W.WEECHAT_RC_OK
|
return W.WEECHAT_RC_OK
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue