Fix all deprecation warnings for 3.8
This commit is contained in:
parent
924398c1ac
commit
a6f61dcbde
@ -223,7 +223,7 @@ class Client:
|
|||||||
|
|
||||||
self._connection.shard_count = self.shard_count
|
self._connection.shard_count = self.shard_count
|
||||||
self._closed = False
|
self._closed = False
|
||||||
self._ready = asyncio.Event(loop=self.loop)
|
self._ready = asyncio.Event()
|
||||||
self._connection._get_websocket = lambda g: self.ws
|
self._connection._get_websocket = lambda g: self.ws
|
||||||
|
|
||||||
if VoiceClient.warn_nacl:
|
if VoiceClient.warn_nacl:
|
||||||
@ -457,7 +457,7 @@ class Client:
|
|||||||
|
|
||||||
async def _connect(self):
|
async def _connect(self):
|
||||||
coro = DiscordWebSocket.from_client(self, shard_id=self.shard_id)
|
coro = DiscordWebSocket.from_client(self, shard_id=self.shard_id)
|
||||||
self.ws = await asyncio.wait_for(coro, timeout=180.0, loop=self.loop)
|
self.ws = await asyncio.wait_for(coro, timeout=180.0)
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
await self.ws.poll_event()
|
await self.ws.poll_event()
|
||||||
@ -466,7 +466,7 @@ class Client:
|
|||||||
self.dispatch('disconnect')
|
self.dispatch('disconnect')
|
||||||
coro = DiscordWebSocket.from_client(self, shard_id=self.shard_id, session=self.ws.session_id,
|
coro = DiscordWebSocket.from_client(self, shard_id=self.shard_id, session=self.ws.session_id,
|
||||||
sequence=self.ws.sequence, resume=True)
|
sequence=self.ws.sequence, resume=True)
|
||||||
self.ws = await asyncio.wait_for(coro, timeout=180.0, loop=self.loop)
|
self.ws = await asyncio.wait_for(coro, timeout=180.0)
|
||||||
|
|
||||||
async def connect(self, *, reconnect=True):
|
async def connect(self, *, reconnect=True):
|
||||||
"""|coro|
|
"""|coro|
|
||||||
@ -528,7 +528,7 @@ class Client:
|
|||||||
|
|
||||||
retry = backoff.delay()
|
retry = backoff.delay()
|
||||||
log.exception("Attempting a reconnect in %.2fs", retry)
|
log.exception("Attempting a reconnect in %.2fs", retry)
|
||||||
await asyncio.sleep(retry, loop=self.loop)
|
await asyncio.sleep(retry)
|
||||||
|
|
||||||
async def close(self):
|
async def close(self):
|
||||||
"""|coro|
|
"""|coro|
|
||||||
@ -866,7 +866,7 @@ class Client:
|
|||||||
self._listeners[ev] = listeners
|
self._listeners[ev] = listeners
|
||||||
|
|
||||||
listeners.append((future, check))
|
listeners.append((future, check))
|
||||||
return asyncio.wait_for(future, timeout, loop=self.loop)
|
return asyncio.wait_for(future, timeout)
|
||||||
|
|
||||||
# event registration
|
# event registration
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ class HTTPClient:
|
|||||||
self.connector = connector
|
self.connector = connector
|
||||||
self.__session = None # filled in static_login
|
self.__session = None # filled in static_login
|
||||||
self._locks = weakref.WeakValueDictionary()
|
self._locks = weakref.WeakValueDictionary()
|
||||||
self._global_over = asyncio.Event(loop=self.loop)
|
self._global_over = asyncio.Event()
|
||||||
self._global_over.set()
|
self._global_over.set()
|
||||||
self.token = None
|
self.token = None
|
||||||
self.bot_token = False
|
self.bot_token = False
|
||||||
@ -104,7 +104,7 @@ class HTTPClient:
|
|||||||
|
|
||||||
def recreate(self):
|
def recreate(self):
|
||||||
if self.__session.closed:
|
if self.__session.closed:
|
||||||
self.__session = aiohttp.ClientSession(connector=self.connector, loop=self.loop)
|
self.__session = aiohttp.ClientSession(connector=self.connector)
|
||||||
|
|
||||||
async def request(self, route, *, files=None, **kwargs):
|
async def request(self, route, *, files=None, **kwargs):
|
||||||
bucket = route.bucket
|
bucket = route.bucket
|
||||||
@ -195,7 +195,7 @@ class HTTPClient:
|
|||||||
log.warning('Global rate limit has been hit. Retrying in %.2f seconds.', retry_after)
|
log.warning('Global rate limit has been hit. Retrying in %.2f seconds.', retry_after)
|
||||||
self._global_over.clear()
|
self._global_over.clear()
|
||||||
|
|
||||||
await asyncio.sleep(retry_after, loop=self.loop)
|
await asyncio.sleep(retry_after)
|
||||||
log.debug('Done sleeping for the rate limit. Retrying...')
|
log.debug('Done sleeping for the rate limit. Retrying...')
|
||||||
|
|
||||||
# release the global lock now that the
|
# release the global lock now that the
|
||||||
@ -208,7 +208,7 @@ class HTTPClient:
|
|||||||
|
|
||||||
# we've received a 500 or 502, unconditional retry
|
# we've received a 500 or 502, unconditional retry
|
||||||
if r.status in {500, 502}:
|
if r.status in {500, 502}:
|
||||||
await asyncio.sleep(1 + tries * 2, loop=self.loop)
|
await asyncio.sleep(1 + tries * 2)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# the usual error cases
|
# the usual error cases
|
||||||
@ -248,7 +248,7 @@ class HTTPClient:
|
|||||||
|
|
||||||
async def static_login(self, token, *, bot):
|
async def static_login(self, token, *, bot):
|
||||||
# Necessary to get aiohttp to stop complaining about session creation
|
# Necessary to get aiohttp to stop complaining about session creation
|
||||||
self.__session = aiohttp.ClientSession(connector=self.connector, loop=self.loop)
|
self.__session = aiohttp.ClientSession(connector=self.connector)
|
||||||
old_token, old_bot = self.token, self.bot_token
|
old_token, old_bot = self.token, self.bot_token
|
||||||
self._token(token, bot=bot)
|
self._token(token, bot=bot)
|
||||||
|
|
||||||
|
@ -132,7 +132,7 @@ class ReactionIterator(_AsyncIterator):
|
|||||||
self.emoji = emoji
|
self.emoji = emoji
|
||||||
self.guild = message.guild
|
self.guild = message.guild
|
||||||
self.channel_id = message.channel.id
|
self.channel_id = message.channel.id
|
||||||
self.users = asyncio.Queue(loop=state.loop)
|
self.users = asyncio.Queue()
|
||||||
|
|
||||||
async def next(self):
|
async def next(self):
|
||||||
if self.users.empty():
|
if self.users.empty():
|
||||||
@ -228,7 +228,7 @@ class HistoryIterator(_AsyncIterator):
|
|||||||
|
|
||||||
self.state = self.messageable._state
|
self.state = self.messageable._state
|
||||||
self.logs_from = self.state.http.logs_from
|
self.logs_from = self.state.http.logs_from
|
||||||
self.messages = asyncio.Queue(loop=self.state.loop)
|
self.messages = asyncio.Queue()
|
||||||
|
|
||||||
if self.around:
|
if self.around:
|
||||||
if self.limit is None:
|
if self.limit is None:
|
||||||
@ -378,7 +378,7 @@ class AuditLogIterator(_AsyncIterator):
|
|||||||
|
|
||||||
self._filter = None # entry dict -> bool
|
self._filter = None # entry dict -> bool
|
||||||
|
|
||||||
self.entries = asyncio.Queue(loop=self.loop)
|
self.entries = asyncio.Queue()
|
||||||
|
|
||||||
|
|
||||||
if self.reverse:
|
if self.reverse:
|
||||||
@ -503,7 +503,7 @@ class GuildIterator(_AsyncIterator):
|
|||||||
|
|
||||||
self.state = self.bot._connection
|
self.state = self.bot._connection
|
||||||
self.get_guilds = self.bot.http.get_guilds
|
self.get_guilds = self.bot.http.get_guilds
|
||||||
self.guilds = asyncio.Queue(loop=self.state.loop)
|
self.guilds = asyncio.Queue()
|
||||||
|
|
||||||
if self.before and self.after:
|
if self.before and self.after:
|
||||||
self._retrieve_guilds = self._retrieve_guilds_before_strategy
|
self._retrieve_guilds = self._retrieve_guilds_before_strategy
|
||||||
@ -600,7 +600,7 @@ class MemberIterator(_AsyncIterator):
|
|||||||
|
|
||||||
self.state = self.guild._state
|
self.state = self.guild._state
|
||||||
self.get_members = self.state.http.get_members
|
self.get_members = self.state.http.get_members
|
||||||
self.members = asyncio.Queue(loop=self.state.loop)
|
self.members = asyncio.Queue()
|
||||||
|
|
||||||
async def next(self):
|
async def next(self):
|
||||||
if self.members.empty():
|
if self.members.empty():
|
||||||
|
@ -707,7 +707,7 @@ class Message:
|
|||||||
"""
|
"""
|
||||||
if delay is not None:
|
if delay is not None:
|
||||||
async def delete():
|
async def delete():
|
||||||
await asyncio.sleep(delay, loop=self._state.loop)
|
await asyncio.sleep(delay)
|
||||||
try:
|
try:
|
||||||
await self._state.http.delete_message(self.channel.id, self.id)
|
await self._state.http.delete_message(self.channel.id, self.id)
|
||||||
except HTTPException:
|
except HTTPException:
|
||||||
|
@ -47,7 +47,7 @@ class Shard:
|
|||||||
self.loop = self._client.loop
|
self.loop = self._client.loop
|
||||||
self._current = self.loop.create_future()
|
self._current = self.loop.create_future()
|
||||||
self._current.set_result(None) # we just need an already done future
|
self._current.set_result(None) # we just need an already done future
|
||||||
self._pending = asyncio.Event(loop=self.loop)
|
self._pending = asyncio.Event()
|
||||||
self._pending_task = None
|
self._pending_task = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -81,7 +81,7 @@ class Shard:
|
|||||||
coro = DiscordWebSocket.from_client(self._client, resume=True, shard_id=self.id,
|
coro = DiscordWebSocket.from_client(self._client, resume=True, shard_id=self.id,
|
||||||
session=self.ws.session_id, sequence=self.ws.sequence)
|
session=self.ws.session_id, sequence=self.ws.sequence)
|
||||||
self._dispatch('disconnect')
|
self._dispatch('disconnect')
|
||||||
self.ws = await asyncio.wait_for(coro, timeout=180.0, loop=self.loop)
|
self.ws = await asyncio.wait_for(coro, timeout=180.0)
|
||||||
|
|
||||||
def get_future(self):
|
def get_future(self):
|
||||||
if self._current.done():
|
if self._current.done():
|
||||||
@ -213,10 +213,10 @@ class AutoShardedClient(Client):
|
|||||||
async def launch_shard(self, gateway, shard_id):
|
async def launch_shard(self, gateway, shard_id):
|
||||||
try:
|
try:
|
||||||
coro = websockets.connect(gateway, loop=self.loop, klass=DiscordWebSocket, compression=None)
|
coro = websockets.connect(gateway, loop=self.loop, klass=DiscordWebSocket, compression=None)
|
||||||
ws = await asyncio.wait_for(coro, loop=self.loop, timeout=180.0)
|
ws = await asyncio.wait_for(coro, timeout=180.0)
|
||||||
except Exception:
|
except Exception:
|
||||||
log.info('Failed to connect for shard_id: %s. Retrying...', shard_id)
|
log.info('Failed to connect for shard_id: %s. Retrying...', shard_id)
|
||||||
await asyncio.sleep(5.0, loop=self.loop)
|
await asyncio.sleep(5.0)
|
||||||
return await self.launch_shard(gateway, shard_id)
|
return await self.launch_shard(gateway, shard_id)
|
||||||
|
|
||||||
ws.token = self.http.token
|
ws.token = self.http.token
|
||||||
@ -230,17 +230,17 @@ class AutoShardedClient(Client):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
# OP HELLO
|
# OP HELLO
|
||||||
await asyncio.wait_for(ws.poll_event(), loop=self.loop, timeout=180.0)
|
await asyncio.wait_for(ws.poll_event(), timeout=180.0)
|
||||||
await asyncio.wait_for(ws.identify(), loop=self.loop, timeout=180.0)
|
await asyncio.wait_for(ws.identify(), timeout=180.0)
|
||||||
except asyncio.TimeoutError:
|
except asyncio.TimeoutError:
|
||||||
log.info('Timed out when connecting for shard_id: %s. Retrying...', shard_id)
|
log.info('Timed out when connecting for shard_id: %s. Retrying...', shard_id)
|
||||||
await asyncio.sleep(5.0, loop=self.loop)
|
await asyncio.sleep(5.0)
|
||||||
return await self.launch_shard(gateway, shard_id)
|
return await self.launch_shard(gateway, shard_id)
|
||||||
|
|
||||||
# keep reading the shard while others connect
|
# keep reading the shard while others connect
|
||||||
self.shards[shard_id] = ret = Shard(ws, self)
|
self.shards[shard_id] = ret = Shard(ws, self)
|
||||||
ret.launch_pending_reads()
|
ret.launch_pending_reads()
|
||||||
await asyncio.sleep(5.0, loop=self.loop)
|
await asyncio.sleep(5.0)
|
||||||
|
|
||||||
async def launch_shards(self):
|
async def launch_shards(self):
|
||||||
if self.shard_count is None:
|
if self.shard_count is None:
|
||||||
@ -261,14 +261,14 @@ class AutoShardedClient(Client):
|
|||||||
shards_to_wait_for.append(shard.wait())
|
shards_to_wait_for.append(shard.wait())
|
||||||
|
|
||||||
# wait for all pending tasks to finish
|
# wait for all pending tasks to finish
|
||||||
await utils.sane_wait_for(shards_to_wait_for, timeout=300.0, loop=self.loop)
|
await utils.sane_wait_for(shards_to_wait_for, timeout=300.0)
|
||||||
|
|
||||||
async def _connect(self):
|
async def _connect(self):
|
||||||
await self.launch_shards()
|
await self.launch_shards()
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
pollers = [shard.get_future() for shard in self.shards.values()]
|
pollers = [shard.get_future() for shard in self.shards.values()]
|
||||||
done, _ = await asyncio.wait(pollers, loop=self.loop, return_when=asyncio.FIRST_COMPLETED)
|
done, _ = await asyncio.wait(pollers, return_when=asyncio.FIRST_COMPLETED)
|
||||||
for f in done:
|
for f in done:
|
||||||
# we wanna re-raise to the main Client.connect handler if applicable
|
# we wanna re-raise to the main Client.connect handler if applicable
|
||||||
f.result()
|
f.result()
|
||||||
@ -291,7 +291,7 @@ class AutoShardedClient(Client):
|
|||||||
|
|
||||||
to_close = [shard.ws.close() for shard in self.shards.values()]
|
to_close = [shard.ws.close() for shard in self.shards.values()]
|
||||||
if to_close:
|
if to_close:
|
||||||
await asyncio.wait(to_close, loop=self.loop)
|
await asyncio.wait(to_close)
|
||||||
|
|
||||||
await self.http.close()
|
await self.http.close()
|
||||||
|
|
||||||
|
@ -304,7 +304,7 @@ class ConnectionState:
|
|||||||
# wait for the chunks
|
# wait for the chunks
|
||||||
if chunks:
|
if chunks:
|
||||||
try:
|
try:
|
||||||
await utils.sane_wait_for(chunks, timeout=len(chunks) * 30.0, loop=self.loop)
|
await utils.sane_wait_for(chunks, timeout=len(chunks) * 30.0)
|
||||||
except asyncio.TimeoutError:
|
except asyncio.TimeoutError:
|
||||||
log.info('Somehow timed out waiting for chunks.')
|
log.info('Somehow timed out waiting for chunks.')
|
||||||
|
|
||||||
@ -323,7 +323,7 @@ class ConnectionState:
|
|||||||
try:
|
try:
|
||||||
# start the query operation
|
# start the query operation
|
||||||
await ws.request_chunks(guild_id, query, limit)
|
await ws.request_chunks(guild_id, query, limit)
|
||||||
members = await asyncio.wait_for(future, timeout=5.0, loop=self.loop)
|
members = await asyncio.wait_for(future, timeout=5.0)
|
||||||
|
|
||||||
if cache:
|
if cache:
|
||||||
for member in members:
|
for member in members:
|
||||||
@ -344,7 +344,7 @@ class ConnectionState:
|
|||||||
# this snippet of code is basically waiting 2 seconds
|
# this snippet of code is basically waiting 2 seconds
|
||||||
# until the last GUILD_CREATE was sent
|
# until the last GUILD_CREATE was sent
|
||||||
launch.set()
|
launch.set()
|
||||||
await asyncio.sleep(2, loop=self.loop)
|
await asyncio.sleep(2)
|
||||||
|
|
||||||
guilds = next(zip(*self._ready_state.guilds), [])
|
guilds = next(zip(*self._ready_state.guilds), [])
|
||||||
if self._fetch_offline:
|
if self._fetch_offline:
|
||||||
@ -694,7 +694,7 @@ class ConnectionState:
|
|||||||
await self.chunker(guild)
|
await self.chunker(guild)
|
||||||
if chunks:
|
if chunks:
|
||||||
try:
|
try:
|
||||||
await utils.sane_wait_for(chunks, timeout=len(chunks), loop=self.loop)
|
await utils.sane_wait_for(chunks, timeout=len(chunks))
|
||||||
except asyncio.TimeoutError:
|
except asyncio.TimeoutError:
|
||||||
log.info('Somehow timed out waiting for chunks.')
|
log.info('Somehow timed out waiting for chunks.')
|
||||||
|
|
||||||
@ -1006,7 +1006,7 @@ class AutoShardedConnectionState(ConnectionState):
|
|||||||
# wait for the chunks
|
# wait for the chunks
|
||||||
if chunks:
|
if chunks:
|
||||||
try:
|
try:
|
||||||
await utils.sane_wait_for(chunks, timeout=len(chunks) * 30.0, loop=self.loop)
|
await utils.sane_wait_for(chunks, timeout=len(chunks) * 30.0)
|
||||||
except asyncio.TimeoutError:
|
except asyncio.TimeoutError:
|
||||||
log.info('Somehow timed out waiting for chunks.')
|
log.info('Somehow timed out waiting for chunks.')
|
||||||
|
|
||||||
@ -1016,7 +1016,7 @@ class AutoShardedConnectionState(ConnectionState):
|
|||||||
# this snippet of code is basically waiting 2 seconds
|
# this snippet of code is basically waiting 2 seconds
|
||||||
# until the last GUILD_CREATE was sent
|
# until the last GUILD_CREATE was sent
|
||||||
launch.set()
|
launch.set()
|
||||||
await asyncio.sleep(2.0 * self.shard_count, loop=self.loop)
|
await asyncio.sleep(2.0 * self.shard_count)
|
||||||
|
|
||||||
if self._fetch_offline:
|
if self._fetch_offline:
|
||||||
guilds = sorted(self._ready_state.guilds, key=lambda g: g[0].shard_id)
|
guilds = sorted(self._ready_state.guilds, key=lambda g: g[0].shard_id)
|
||||||
|
@ -327,8 +327,8 @@ async def async_all(gen, *, check=_isawaitable):
|
|||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
async def sane_wait_for(futures, *, timeout, loop):
|
async def sane_wait_for(futures, *, timeout):
|
||||||
done, pending = await asyncio.wait(futures, timeout=timeout, return_when=asyncio.ALL_COMPLETED, loop=loop)
|
done, pending = await asyncio.wait(futures, timeout=timeout, return_when=asyncio.ALL_COMPLETED)
|
||||||
|
|
||||||
if len(pending) != 0:
|
if len(pending) != 0:
|
||||||
raise asyncio.TimeoutError()
|
raise asyncio.TimeoutError()
|
||||||
|
@ -101,8 +101,8 @@ class VoiceClient:
|
|||||||
self._connected = threading.Event()
|
self._connected = threading.Event()
|
||||||
|
|
||||||
self._handshaking = False
|
self._handshaking = False
|
||||||
self._handshake_check = asyncio.Lock(loop=self.loop)
|
self._handshake_check = asyncio.Lock()
|
||||||
self._handshake_complete = asyncio.Event(loop=self.loop)
|
self._handshake_complete = asyncio.Event()
|
||||||
|
|
||||||
self.mode = None
|
self.mode = None
|
||||||
self._connections = 0
|
self._connections = 0
|
||||||
@ -149,7 +149,7 @@ class VoiceClient:
|
|||||||
await ws.voice_state(guild_id, channel_id)
|
await ws.voice_state(guild_id, channel_id)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await asyncio.wait_for(self._handshake_complete.wait(), timeout=self.timeout, loop=self.loop)
|
await asyncio.wait_for(self._handshake_complete.wait(), timeout=self.timeout)
|
||||||
except asyncio.TimeoutError:
|
except asyncio.TimeoutError:
|
||||||
await self.terminate_handshake(remove=True)
|
await self.terminate_handshake(remove=True)
|
||||||
raise
|
raise
|
||||||
@ -225,7 +225,7 @@ class VoiceClient:
|
|||||||
except (ConnectionClosed, asyncio.TimeoutError):
|
except (ConnectionClosed, asyncio.TimeoutError):
|
||||||
if reconnect and _tries < 5:
|
if reconnect and _tries < 5:
|
||||||
log.exception('Failed to connect to voice... Retrying...')
|
log.exception('Failed to connect to voice... Retrying...')
|
||||||
await asyncio.sleep(1 + _tries * 2.0, loop=self.loop)
|
await asyncio.sleep(1 + _tries * 2.0)
|
||||||
await self.terminate_handshake()
|
await self.terminate_handshake()
|
||||||
await self.connect(reconnect=reconnect, _tries=_tries + 1)
|
await self.connect(reconnect=reconnect, _tries=_tries + 1)
|
||||||
else:
|
else:
|
||||||
@ -257,7 +257,7 @@ class VoiceClient:
|
|||||||
retry = backoff.delay()
|
retry = backoff.delay()
|
||||||
log.exception('Disconnected from voice... Reconnecting in %.2fs.', retry)
|
log.exception('Disconnected from voice... Reconnecting in %.2fs.', retry)
|
||||||
self._connected.clear()
|
self._connected.clear()
|
||||||
await asyncio.sleep(retry, loop=self.loop)
|
await asyncio.sleep(retry)
|
||||||
await self.terminate_handshake()
|
await self.terminate_handshake()
|
||||||
try:
|
try:
|
||||||
await self.connect(reconnect=True)
|
await self.connect(reconnect=True)
|
||||||
|
@ -201,7 +201,7 @@ class AsyncWebhookAdapter(WebhookAdapter):
|
|||||||
remaining = r.headers.get('X-Ratelimit-Remaining')
|
remaining = r.headers.get('X-Ratelimit-Remaining')
|
||||||
if remaining == '0' and r.status != 429:
|
if remaining == '0' and r.status != 429:
|
||||||
delta = utils._parse_ratelimit_header(r)
|
delta = utils._parse_ratelimit_header(r)
|
||||||
await asyncio.sleep(delta, loop=self.loop)
|
await asyncio.sleep(delta)
|
||||||
|
|
||||||
if 300 > r.status >= 200:
|
if 300 > r.status >= 200:
|
||||||
return response
|
return response
|
||||||
@ -209,11 +209,11 @@ class AsyncWebhookAdapter(WebhookAdapter):
|
|||||||
# we are being rate limited
|
# we are being rate limited
|
||||||
if r.status == 429:
|
if r.status == 429:
|
||||||
retry_after = response['retry_after'] / 1000.0
|
retry_after = response['retry_after'] / 1000.0
|
||||||
await asyncio.sleep(retry_after, loop=self.loop)
|
await asyncio.sleep(retry_after)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if r.status in (500, 502):
|
if r.status in (500, 502):
|
||||||
await asyncio.sleep(1 + tries * 2, loop=self.loop)
|
await asyncio.sleep(1 + tries * 2)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if r.status == 403:
|
if r.status == 403:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user