server: Add a maximum value for the number of nicklist users.

This commit is contained in:
Damir Jelić 2018-10-30 21:12:41 +01:00
parent d896962b19
commit 74efe8ef9b
5 changed files with 122 additions and 35 deletions

11
main.py
View file

@ -34,9 +34,14 @@ from logbook import Logger, StreamHandler
from nio import RemoteProtocolError, RemoteTransportError, TransportType
from matrix import globals as G
from matrix.bar_items import (init_bar_items, matrix_bar_item_buffer_modes,
matrix_bar_item_lag, matrix_bar_item_name,
matrix_bar_item_plugin)
from matrix.bar_items import (
init_bar_items,
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
# Weechat searches for the registered callbacks in the scope of the main script
# file, import the callbacks here so weechat can find them.

View file

@ -117,9 +117,26 @@ def matrix_bar_item_buffer_modes(data, item, window, buffer, extra_info):
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():
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)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)matrix_modes", "matrix_bar_item_buffer_modes", "")

View file

@ -692,7 +692,7 @@ class WeechatChannelBuffer(object):
message = self._membership_message(user, "invite")
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
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):
# type: (str, int, bool, str, List[str]) -> None
user = self._get_user(nick)
self._remove_user_from_nicklist(user)
self.remove_user_from_nicklist(user)
if len(self.users) <= 2:
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.members_fetched = False
self.unhandled_users = [] # type: List[str]
self.inactive_users = []
buffer_name = "{}.{}".format(server_name, room.room_id)
@ -840,7 +841,11 @@ class RoomBuffer(object):
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:
user = self.room.users[user_id]
except KeyError:
@ -848,9 +853,24 @@ class RoomBuffer(object):
# yet to come, so do nothing
return
# User is already added don't add him again.
if user_id in self.displayed_nicks:
return
# Adding users to the nicklist is a O(1) + search time
# 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
try:
self.inactive_users.remove(user_id)
except ValueError:
pass
short_name = shorten_sender(user.user_id)
@ -882,28 +902,13 @@ class RoomBuffer(object):
date = server_ts_to_weechat(event.server_timestamp)
if event.content["membership"] == "join":
if event.state_key not in self.displayed_nicks:
# Adding users to the nicklist is a O(1) + search time
# 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 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)
else:
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)
if (event.state_key not in self.displayed_nicks
and event.state_key not in self.inactive_users):
if len(self.room.users) > 100:
self.unhandled_users.append(event.state_key)
return
self.add_user(event.state_key, date, is_state)
else:
# TODO print out profile changes
return
@ -1062,7 +1067,7 @@ class RoomBuffer(object):
# There is no way to change the group of a user without
# 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)
def handle_state_event(self, event):
@ -1087,7 +1092,7 @@ class RoomBuffer(object):
except ValueError:
pass
self.add_user(event.sender, 0, True)
self.add_user(event.sender, 0, True, True)
if isinstance(event, RoomMemberEvent):
self.handle_membership_events(event, False)

View file

@ -396,6 +396,18 @@ class MatrixConfig(WeechatConfig):
"they will be loaded when the buffer becomes active. This "
"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 = [

View file

@ -252,6 +252,7 @@ class MatrixServer(object):
# type: DefaultDict[str, Deque[EncrytpionQueueItem]]
self.backlog_queue = dict() # type: Dict[str, str]
self.user_gc_time = time.time() # type: float
self.member_request_list = [] # type: List[str]
self.rooms_with_missing_members = [] # type: List[str]
self.lazy_load_hook = None # type: Optional[str]
@ -1101,6 +1102,53 @@ class MatrixServer(object):
room_buffer = self.room_buffers[room_id]
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):
if not self.server_buffer:
return
@ -1267,8 +1315,8 @@ def matrix_timer_cb(server_name, remaining_calls):
}
server.sync(timeout, sync_filter)
if not server.next_batch:
return W.WEECHAT_RC_OK
if current_time > (server.user_gc_time + 3600):
server.garbage_collect_users()
return W.WEECHAT_RC_OK