broken peers list
This commit is contained in:
parent
dcc3a3dcfa
commit
02af0f7671
@ -324,7 +324,7 @@ class App:
|
|||||||
self._file_transfer_handler = FileTransfersHandler(self._tox, self._settings, self._contacts_provider,
|
self._file_transfer_handler = FileTransfersHandler(self._tox, self._settings, self._contacts_provider,
|
||||||
file_transfers_message_service, profile)
|
file_transfers_message_service, profile)
|
||||||
messages_items_factory.set_file_transfers_handler(self._file_transfer_handler)
|
messages_items_factory.set_file_transfers_handler(self._file_transfer_handler)
|
||||||
self._groups_service = GroupsService(self._tox, self._contacts_manager, self._contacts_provider)
|
self._groups_service = GroupsService(self._tox, self._contacts_manager, self._contacts_provider, self._ms)
|
||||||
widgets_factory = WidgetsFactory(self._settings, profile, self._profile_manager, self._contacts_manager,
|
widgets_factory = WidgetsFactory(self._settings, profile, self._profile_manager, self._contacts_manager,
|
||||||
self._file_transfer_handler, self._smiley_loader, self._plugin_loader,
|
self._file_transfer_handler, self._smiley_loader, self._plugin_loader,
|
||||||
self._toxes, self._version, self._groups_service)
|
self._toxes, self._version, self._groups_service)
|
||||||
|
@ -31,12 +31,13 @@ class GroupChat(contact.Contact):
|
|||||||
def get_self_name(self):
|
def get_self_name(self):
|
||||||
return self._peers[0].name
|
return self._peers[0].name
|
||||||
|
|
||||||
def add_peer(self, peer_id):
|
def add_peer(self, peer_id, is_current_user=False):
|
||||||
peer = GroupChatPeer(peer_id,
|
peer = GroupChatPeer(peer_id,
|
||||||
self._tox.group_peer_get_name(self._number, peer_id),
|
self._tox.group_peer_get_name(self._number, peer_id),
|
||||||
self._tox.group_peer_get_status(self._number, peer_id),
|
self._tox.group_peer_get_status(self._number, peer_id),
|
||||||
self._tox.group_peer_get_role(self._number, peer_id),
|
self._tox.group_peer_get_role(self._number, peer_id),
|
||||||
self._tox.group_peer_get_public_key(self._number, peer_id))
|
self._tox.group_peer_get_public_key(self._number, peer_id),
|
||||||
|
is_current_user)
|
||||||
self._peers.append(peer)
|
self._peers.append(peer)
|
||||||
|
|
||||||
def get_peer_by_id(self, peer_id):
|
def get_peer_by_id(self, peer_id):
|
||||||
@ -44,6 +45,11 @@ class GroupChat(contact.Contact):
|
|||||||
|
|
||||||
return peers[0]
|
return peers[0]
|
||||||
|
|
||||||
|
def get_peers(self):
|
||||||
|
return self._peers[:]
|
||||||
|
|
||||||
|
peers = property(get_peers)
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Private methods
|
# Private methods
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
@ -54,4 +60,4 @@ class GroupChat(contact.Contact):
|
|||||||
|
|
||||||
def _add_self_to_gc(self):
|
def _add_self_to_gc(self):
|
||||||
peer_id = self._tox.group_self_get_peer_id(self._number)
|
peer_id = self._tox.group_self_get_peer_id(self._number)
|
||||||
self.add_peer(peer_id)
|
self.add_peer(peer_id, True)
|
||||||
|
@ -2,12 +2,13 @@
|
|||||||
|
|
||||||
class GroupChatPeer:
|
class GroupChatPeer:
|
||||||
|
|
||||||
def __init__(self, peer_id, name, status, role, public_key):
|
def __init__(self, peer_id, name, status, role, public_key, is_current_user):
|
||||||
self._peer_id = peer_id
|
self._peer_id = peer_id
|
||||||
self._name = name
|
self._name = name
|
||||||
self._status = status
|
self._status = status
|
||||||
self._role = role
|
self._role = role
|
||||||
self._public_key = public_key
|
self._public_key = public_key
|
||||||
|
self._is_current_user = is_current_user
|
||||||
|
|
||||||
def get_id(self):
|
def get_id(self):
|
||||||
return self._peer_id
|
return self._peer_id
|
||||||
@ -42,3 +43,8 @@ class GroupChatPeer:
|
|||||||
return self._public_key
|
return self._public_key
|
||||||
|
|
||||||
public_key = property(get_public_key)
|
public_key = property(get_public_key)
|
||||||
|
|
||||||
|
def get_is_current_user(self):
|
||||||
|
return self._is_current_user
|
||||||
|
|
||||||
|
is_current_user = property(get_is_current_user)
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
import common.tox_save as tox_save
|
import common.tox_save as tox_save
|
||||||
import utils.ui as util_ui
|
import utils.ui as util_ui
|
||||||
|
from groups.peers_list import PeersListGenerator
|
||||||
|
|
||||||
|
|
||||||
class GroupsService(tox_save.ToxSave):
|
class GroupsService(tox_save.ToxSave):
|
||||||
|
|
||||||
def __init__(self, tox, contacts_manager, contacts_provider):
|
def __init__(self, tox, contacts_manager, contacts_provider, main_screen):
|
||||||
super().__init__(tox)
|
super().__init__(tox)
|
||||||
self._contacts_manager = contacts_manager
|
self._contacts_manager = contacts_manager
|
||||||
self._contacts_provider = contacts_provider
|
self._contacts_provider = contacts_provider
|
||||||
|
self._peers_list_widget = main_screen.peers_list
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Groups creation
|
# Groups creation
|
||||||
@ -56,6 +58,17 @@ class GroupsService(tox_save.ToxSave):
|
|||||||
group.name = self._tox.group_get_name(group.number)
|
group.name = self._tox.group_get_name(group.number)
|
||||||
group.status_message = self._tox.group_get_topic(group.number)
|
group.status_message = self._tox.group_get_topic(group.number)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
# Peers list
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def generate_peers_list(self):
|
||||||
|
group = self._contacts_manager.get_curr_contact()
|
||||||
|
PeersListGenerator().generate(group.peers, self, self._peers_list_widget, group.tox_id)
|
||||||
|
|
||||||
|
def peer_selected(self, chat_id, peer_id):
|
||||||
|
pass
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Private methods
|
# Private methods
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
99
toxygen/groups/peers_list.py
Normal file
99
toxygen/groups/peers_list.py
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
from PyQt5 import QtWidgets, QtCore
|
||||||
|
from ui.group_peers_list import PeerItem, PeerTypeItem
|
||||||
|
import utils.ui as util_ui
|
||||||
|
from wrapper.toxcore_enums_and_consts import *
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
# Builder
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
class PeerListBuilder:
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._peers = {}
|
||||||
|
self._titles = {}
|
||||||
|
self._index = 0
|
||||||
|
self._handler = None
|
||||||
|
|
||||||
|
def with_click_handler(self, handler):
|
||||||
|
self._handler = handler
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
||||||
|
def with_title(self, title):
|
||||||
|
self._titles[self._index] = title
|
||||||
|
self._index += 1
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
||||||
|
def with_peers(self, peers):
|
||||||
|
for peer in peers:
|
||||||
|
self._add_peer(peer)
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
||||||
|
def build(self, parent):
|
||||||
|
parent.clear()
|
||||||
|
|
||||||
|
for i in range(self._index):
|
||||||
|
if i in self._peers:
|
||||||
|
peer = self._peers[i]
|
||||||
|
self._add_peer_item(peer, parent)
|
||||||
|
else:
|
||||||
|
title = self._titles[i]
|
||||||
|
self._add_peer_type_item(title, parent)
|
||||||
|
|
||||||
|
return parent
|
||||||
|
|
||||||
|
def _add_peer_item(self, peer, parent):
|
||||||
|
item = PeerItem(peer, self._handler, parent.width())
|
||||||
|
self._add_item(parent, item)
|
||||||
|
|
||||||
|
def _add_peer_type_item(self, text, parent):
|
||||||
|
item = PeerTypeItem(text, parent.width())
|
||||||
|
self._add_item(parent, item)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _add_item(parent, item):
|
||||||
|
elem = QtWidgets.QListWidgetItem()
|
||||||
|
elem.setSizeHint(QtCore.QSize(parent.width(), item.height()))
|
||||||
|
parent.addItem(elem)
|
||||||
|
parent.setItemWidget(elem, item)
|
||||||
|
|
||||||
|
def _add_peer(self, peer):
|
||||||
|
self._peers[self._index] = peer
|
||||||
|
self._index += 1
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
# Generators
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
class PeersListGenerator:
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def generate(peers_list, groups_service, parent, chat_id):
|
||||||
|
admin_title = util_ui.tr('Administrator')
|
||||||
|
moderators_title = util_ui.tr('Moderators')
|
||||||
|
users_title = util_ui.tr('Users')
|
||||||
|
observers_title = util_ui.tr('Observers')
|
||||||
|
|
||||||
|
admins = list(filter(lambda p: p.role == TOX_GROUP_ROLE['FOUNDER'], peers_list))
|
||||||
|
moderators = list(filter(lambda p: p.role == TOX_GROUP_ROLE['MODERATOR'], peers_list))
|
||||||
|
users = list(filter(lambda p: p.role == TOX_GROUP_ROLE['USER'], peers_list))
|
||||||
|
observers = list(filter(lambda p: p.role == TOX_GROUP_ROLE['OBSERVER'], peers_list))
|
||||||
|
|
||||||
|
builder = (PeerListBuilder()
|
||||||
|
.with_click_handler(lambda peer_id: groups_service.peer_selected(chat_id, peer_id))
|
||||||
|
.with_title(admin_title)
|
||||||
|
.with_peers(admins)
|
||||||
|
.with_title(moderators_title)
|
||||||
|
.with_peers(moderators)
|
||||||
|
.with_title(users_title)
|
||||||
|
.with_peers(users)
|
||||||
|
.with_title(observers_title)
|
||||||
|
.with_peers(observers)
|
||||||
|
)
|
||||||
|
|
||||||
|
return builder.build(parent)
|
33
toxygen/ui/group_peers_list.py
Normal file
33
toxygen/ui/group_peers_list.py
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
from ui.widgets import *
|
||||||
|
from wrapper.toxcore_enums_and_consts import *
|
||||||
|
|
||||||
|
|
||||||
|
class PeerItem(QtWidgets.QWidget):
|
||||||
|
|
||||||
|
def __init__(self, peer, handler, width, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.resize(QtCore.QSize(width, 34))
|
||||||
|
self.nameLabel = DataLabel(self)
|
||||||
|
self.nameLabel.setGeometry(0, 0, width, 34)
|
||||||
|
name = peer.name
|
||||||
|
if peer.is_current_user:
|
||||||
|
name += util_ui.tr(' (You)')
|
||||||
|
self.nameLabel.setText(name)
|
||||||
|
if peer.status == TOX_USER_STATUS['NONE']:
|
||||||
|
style = 'QLabel {color: green}'
|
||||||
|
elif peer.status == TOX_USER_STATUS['AWAY']:
|
||||||
|
style = 'QLabel {color: yellow}'
|
||||||
|
else:
|
||||||
|
style = 'QLabel {color: red}'
|
||||||
|
self.nameLabel.setStyleSheet(style)
|
||||||
|
self.nameLabel.mousePressEvent = lambda x: handler(peer.id)
|
||||||
|
|
||||||
|
|
||||||
|
class PeerTypeItem(QtWidgets.QWidget):
|
||||||
|
|
||||||
|
def __init__(self, text, width, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.resize(QtCore.QSize(width, 34))
|
||||||
|
self.nameLabel = DataLabel(self)
|
||||||
|
self.nameLabel.setGeometry(0, 0, width, 34)
|
||||||
|
self.nameLabel.setText(text)
|
@ -279,7 +279,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
self.videocallButton.clicked.connect(lambda: self._calls_manager.call_click(True, True))
|
self.videocallButton.clicked.connect(lambda: self._calls_manager.call_click(True, True))
|
||||||
self.groupMenuButton = QtWidgets.QPushButton(Form)
|
self.groupMenuButton = QtWidgets.QPushButton(Form)
|
||||||
self.groupMenuButton.setGeometry(QtCore.QRect(470, 10, 50, 50))
|
self.groupMenuButton.setGeometry(QtCore.QRect(470, 10, 50, 50))
|
||||||
self.groupMenuButton.clicked.connect(self._show_gc_peers_list)
|
self.groupMenuButton.clicked.connect(self._toggle_gc_peers_list)
|
||||||
self.groupMenuButton.setVisible(False)
|
self.groupMenuButton.setVisible(False)
|
||||||
pixmap = QtGui.QPixmap(util.join_path(util.get_images_directory(), 'menu.png'))
|
pixmap = QtGui.QPixmap(util.join_path(util.get_images_directory(), 'menu.png'))
|
||||||
icon = QtGui.QIcon(pixmap)
|
icon = QtGui.QIcon(pixmap)
|
||||||
@ -325,6 +325,13 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
self.messages.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
|
self.messages.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
|
||||||
self.messages.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
|
self.messages.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
|
||||||
|
|
||||||
|
self.peers_list = QtWidgets.QListWidget(widget)
|
||||||
|
self.peers_list.setGeometry(0, 0, 0, 0)
|
||||||
|
self.peers_list.setObjectName("peersList")
|
||||||
|
self.peers_list.setSpacing(1)
|
||||||
|
self.peers_list.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
|
||||||
|
self.peers_list.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
||||||
|
|
||||||
def initUI(self):
|
def initUI(self):
|
||||||
self.setMinimumSize(920, 500)
|
self.setMinimumSize(920, 500)
|
||||||
s = self._settings
|
s = self._settings
|
||||||
@ -403,14 +410,18 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
def resizeEvent(self, *args, **kwargs):
|
def resizeEvent(self, *args, **kwargs):
|
||||||
|
width = self.width() - 270
|
||||||
if not self._should_show_group_peers_list:
|
if not self._should_show_group_peers_list:
|
||||||
self.messages.setGeometry(0, 0, self.width() - 270, self.height() - 155)
|
self.messages.setGeometry(0, 0, width, self.height() - 155)
|
||||||
|
self.peers_list.setGeometry(0, 0, 0, 0)
|
||||||
else:
|
else:
|
||||||
self.messages.setGeometry(0, 0, self.width() - 450, self.height() - 155)
|
self.messages.setGeometry(0, 0, width * 3 // 4, self.height() - 155)
|
||||||
|
self.peers_list.setGeometry(width * 3 // 4, 0, width - width * 3 // 4, self.height() - 155)
|
||||||
self.friends_list.setGeometry(0, 0, 270, self.height() - 125)
|
self.friends_list.setGeometry(0, 0, 270, self.height() - 125)
|
||||||
|
|
||||||
self.videocallButton.setGeometry(QtCore.QRect(self.width() - 330, 10, 50, 50))
|
self.videocallButton.setGeometry(QtCore.QRect(self.width() - 330, 10, 50, 50))
|
||||||
self.callButton.setGeometry(QtCore.QRect(self.width() - 390, 10, 50, 50))
|
self.callButton.setGeometry(QtCore.QRect(self.width() - 390, 10, 50, 50))
|
||||||
|
self.groupMenuButton.setGeometry(QtCore.QRect(self.width() - 450, 10, 50, 50))
|
||||||
self.typing.setGeometry(QtCore.QRect(self.width() - 450, 20, 50, 30))
|
self.typing.setGeometry(QtCore.QRect(self.width() - 450, 20, 50, 30))
|
||||||
|
|
||||||
self.messageEdit.setGeometry(QtCore.QRect(55, 0, self.width() - 395, 55))
|
self.messageEdit.setGeometry(QtCore.QRect(55, 0, self.width() - 395, 55))
|
||||||
@ -678,6 +689,8 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
num = index.row()
|
num = index.row()
|
||||||
self._contacts_manager.set_active(num)
|
self._contacts_manager.set_active(num)
|
||||||
self.groupMenuButton.setVisible(not self._contacts_manager.is_active_a_friend())
|
self.groupMenuButton.setVisible(not self._contacts_manager.is_active_a_friend())
|
||||||
|
if self._should_show_group_peers_list:
|
||||||
|
self._toggle_gc_peers_list()
|
||||||
self.resizeEvent()
|
self.resizeEvent()
|
||||||
|
|
||||||
def mouseReleaseEvent(self, event):
|
def mouseReleaseEvent(self, event):
|
||||||
@ -704,6 +717,8 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
self.messages.setGeometry(x, self.messages.y(), self.messages.width(), self.messages.height() - 40)
|
self.messages.setGeometry(x, self.messages.y(), self.messages.width(), self.messages.height() - 40)
|
||||||
self.search_field.show()
|
self.search_field.show()
|
||||||
|
|
||||||
def _show_gc_peers_list(self):
|
def _toggle_gc_peers_list(self):
|
||||||
self._should_show_group_peers_list = not self._should_show_group_peers_list
|
self._should_show_group_peers_list = not self._should_show_group_peers_list
|
||||||
|
if self._should_show_group_peers_list:
|
||||||
|
self._groups_service.generate_peers_list()
|
||||||
self.resizeEvent()
|
self.resizeEvent()
|
||||||
|
@ -198,7 +198,7 @@ TOX_GROUP_PRIVACY_STATE = {
|
|||||||
# and peer ID's (but not Tox ID's) is visible to anyone with access to a node
|
# and peer ID's (but not Tox ID's) is visible to anyone with access to a node
|
||||||
# storing a DHT entry for the given group.
|
# storing a DHT entry for the given group.
|
||||||
#
|
#
|
||||||
'TOX_GROUP_PRIVACY_STATE_PUBLIC': 0,
|
'PUBLIC': 0,
|
||||||
|
|
||||||
#
|
#
|
||||||
# The group is considered to be private. The only way to join the group is by having
|
# The group is considered to be private. The only way to join the group is by having
|
||||||
@ -208,7 +208,7 @@ TOX_GROUP_PRIVACY_STATE = {
|
|||||||
# the DHT is not used for any purpose at all. If a public group is set to private,
|
# the DHT is not used for any purpose at all. If a public group is set to private,
|
||||||
# all DHT information related to the group will expire shortly.
|
# all DHT information related to the group will expire shortly.
|
||||||
#
|
#
|
||||||
'TOX_GROUP_PRIVACY_STATE_PRIVATE': 1
|
'PRIVATE': 1
|
||||||
}
|
}
|
||||||
|
|
||||||
TOX_GROUP_ROLE = {
|
TOX_GROUP_ROLE = {
|
||||||
@ -217,23 +217,23 @@ TOX_GROUP_ROLE = {
|
|||||||
# May kick and ban all other peers as well as set their role to anything (except founder).
|
# May kick and ban all other peers as well as set their role to anything (except founder).
|
||||||
# Founders may also set the group password, toggle the privacy state, and set the peer limit.
|
# Founders may also set the group password, toggle the privacy state, and set the peer limit.
|
||||||
#
|
#
|
||||||
'TOX_GROUP_ROLE_FOUNDER': 0,
|
'FOUNDER': 0,
|
||||||
|
|
||||||
#
|
#
|
||||||
# May kick, ban and set the user and observer roles for peers below this role.
|
# May kick, ban and set the user and observer roles for peers below this role.
|
||||||
# May also set the group topic.
|
# May also set the group topic.
|
||||||
#
|
#
|
||||||
'TOX_GROUP_ROLE_MODERATOR': 1,
|
'MODERATOR': 1,
|
||||||
|
|
||||||
#
|
#
|
||||||
# May communicate with other peers normally.
|
# May communicate with other peers normally.
|
||||||
#
|
#
|
||||||
'TOX_GROUP_ROLE_USER': 2,
|
'USER': 2,
|
||||||
|
|
||||||
#
|
#
|
||||||
# May observe the group and ignore peers; may not communicate with other peers or with the group.
|
# May observe the group and ignore peers; may not communicate with other peers or with the group.
|
||||||
#
|
#
|
||||||
'TOX_GROUP_ROLE_OBSERVER': 3
|
'OBSERVER': 3
|
||||||
}
|
}
|
||||||
|
|
||||||
TOX_ERR_GROUP_NEW = {
|
TOX_ERR_GROUP_NEW = {
|
||||||
@ -870,27 +870,27 @@ TOX_GROUP_MOD_EVENT = {
|
|||||||
#
|
#
|
||||||
# A peer has been kicked from the group.
|
# A peer has been kicked from the group.
|
||||||
#
|
#
|
||||||
'TOX_GROUP_MOD_EVENT_KICK': 0,
|
'KICK': 0,
|
||||||
|
|
||||||
#
|
#
|
||||||
# A peer has been banned from the group.
|
# A peer has been banned from the group.
|
||||||
#
|
#
|
||||||
'TOX_GROUP_MOD_EVENT_BAN': 1,
|
'BAN': 1,
|
||||||
|
|
||||||
#
|
#
|
||||||
# A peer as been given the observer role.
|
# A peer as been given the observer role.
|
||||||
#
|
#
|
||||||
'TOX_GROUP_MOD_EVENT_OBSERVER': 2,
|
'OBSERVER': 2,
|
||||||
|
|
||||||
#
|
#
|
||||||
# A peer has been given the user role.
|
# A peer has been given the user role.
|
||||||
#
|
#
|
||||||
'TOX_GROUP_MOD_EVENT_USER': 3,
|
'USER': 3,
|
||||||
|
|
||||||
#
|
#
|
||||||
# A peer has been given the moderator role.
|
# A peer has been given the moderator role.
|
||||||
#
|
#
|
||||||
'TOX_GROUP_MOD_EVENT_MODERATOR': 4,
|
'MODERATOR': 4,
|
||||||
}
|
}
|
||||||
|
|
||||||
TOX_ERR_GROUP_BAN_QUERY = {
|
TOX_ERR_GROUP_BAN_QUERY = {
|
||||||
|
Loading…
Reference in New Issue
Block a user