mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2026-06-19 07:35:19 +00:00
Refactor TestStickerSetWithRequest tests (#5161)
This commit is contained in:
@@ -20,7 +20,6 @@
|
||||
|
||||
import pytest
|
||||
|
||||
from telegram.error import BadRequest
|
||||
from tests.auxil.files import data_file
|
||||
from tests.auxil.networking import expect_bad_request
|
||||
|
||||
@@ -51,20 +50,6 @@ def animated_sticker_file():
|
||||
yield f
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def animated_sticker_set(bot):
|
||||
ss = await bot.get_sticker_set(f"animated_test_by_{bot.username}")
|
||||
if len(ss.stickers) > 100:
|
||||
try:
|
||||
for i in range(1, 50):
|
||||
await bot.delete_sticker_from_set(ss.stickers[-i].file_id)
|
||||
except BadRequest as e:
|
||||
if e.message == "Stickerset_not_modified":
|
||||
return ss
|
||||
raise Exception("stickerset is growing too large.") from None
|
||||
return ss
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
async def audio(bot, chat_id):
|
||||
with data_file("telegram.mp3").open("rb") as f, data_file("thumb.jpg").open("rb") as thumb:
|
||||
@@ -127,20 +112,6 @@ def sticker_file():
|
||||
yield file
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def sticker_set(bot):
|
||||
ss = await bot.get_sticker_set(f"test_by_{bot.username}")
|
||||
if len(ss.stickers) > 100:
|
||||
try:
|
||||
for i in range(1, 50):
|
||||
await bot.delete_sticker_from_set(ss.stickers[-i].file_id)
|
||||
except BadRequest as e:
|
||||
if e.message == "Stickerset_not_modified":
|
||||
return ss
|
||||
raise Exception("stickerset is growing too large.") from None
|
||||
return ss
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sticker_set_thumb_file():
|
||||
with data_file("sticker_set_thumb.png").open("rb") as file:
|
||||
@@ -174,17 +145,3 @@ def video_sticker_file():
|
||||
def video_sticker(bot, chat_id):
|
||||
with data_file("telegram_video_sticker.webm").open("rb") as f:
|
||||
return bot.send_sticker(chat_id, sticker=f, timeout=50).sticker
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def video_sticker_set(bot):
|
||||
ss = await bot.get_sticker_set(f"video_test_by_{bot.username}")
|
||||
if len(ss.stickers) > 100:
|
||||
try:
|
||||
for i in range(1, 50):
|
||||
await bot.delete_sticker_from_set(ss.stickers[-i].file_id)
|
||||
except BadRequest as e:
|
||||
if e.message == "Stickerset_not_modified":
|
||||
return ss
|
||||
raise Exception("stickerset is growing too large.") from None
|
||||
return ss
|
||||
|
||||
+155
-190
@@ -493,6 +493,7 @@ class StickerSetTestBase:
|
||||
name = "NOTAREALNAME"
|
||||
sticker_type = Sticker.REGULAR
|
||||
contains_masks = True
|
||||
thumbnail = PhotoSize("thumb_file_id", "thumb_file_un_id", 100, 100, False)
|
||||
|
||||
|
||||
class TestStickerSetWithoutRequest(StickerSetTestBase):
|
||||
@@ -521,7 +522,14 @@ class TestStickerSetWithoutRequest(StickerSetTestBase):
|
||||
assert sticker_set.sticker_type == self.sticker_type
|
||||
assert sticker_set.api_kwargs == {"contains_masks": self.contains_masks}
|
||||
|
||||
def test_sticker_set_to_dict(self, sticker_set):
|
||||
def test_sticker_set_to_dict(self):
|
||||
sticker_set = StickerSet(
|
||||
self.name,
|
||||
self.title,
|
||||
self.stickers,
|
||||
self.sticker_type,
|
||||
thumbnail=self.thumbnail,
|
||||
)
|
||||
sticker_set_dict = sticker_set.to_dict()
|
||||
|
||||
assert isinstance(sticker_set_dict, dict)
|
||||
@@ -752,71 +760,86 @@ class TestStickerSetWithoutRequest(StickerSetTestBase):
|
||||
assert await offline_bot.set_sticker_keywords(sticker, ["keyword"])
|
||||
|
||||
|
||||
@pytest.mark.xdist_group("stickerset")
|
||||
class TestStickerSetWithRequest:
|
||||
async def test_create_sticker_set(
|
||||
self, bot, chat_id, sticker_file, animated_sticker_file, video_sticker_file
|
||||
# In-process guard: once a worker has set up its own sticker set, skip on subsequent tests.
|
||||
_WORKER_CREATED_STICKER_SET = False
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
async def _create_sticker_set(
|
||||
self,
|
||||
bot,
|
||||
chat_id,
|
||||
sticker_file,
|
||||
animated_sticker_file,
|
||||
video_sticker_file,
|
||||
sticker_set_name,
|
||||
worker_id,
|
||||
):
|
||||
"""Creates the sticker set (if needed) which is required for tests. Make sure that this
|
||||
test comes before the tests that actually use the sticker sets!
|
||||
"""Creates the sticker set (if needed) which is required for all tests below. Runs exactly
|
||||
once during setup for every xdist worker, so we don't have to worry about the sticker set
|
||||
not being there for the tests that need it.
|
||||
"""
|
||||
test_by = f"test_by_{bot.username}"
|
||||
for sticker_set in [test_by, f"animated_{test_by}", f"video_{test_by}"]:
|
||||
try:
|
||||
ss = await bot.get_sticker_set(sticker_set)
|
||||
assert isinstance(ss, StickerSet)
|
||||
if self._WORKER_CREATED_STICKER_SET:
|
||||
return
|
||||
|
||||
if len(ss.stickers) > 100:
|
||||
try:
|
||||
for i in range(1, 50):
|
||||
await bot.delete_sticker_from_set(ss.stickers[-i].file_id)
|
||||
except BadRequest as e:
|
||||
if e.message != "Stickerset_not_modified":
|
||||
raise Exception("stickerset is growing too large.") from None
|
||||
except BadRequest as e:
|
||||
if not e.message == "Stickerset_invalid":
|
||||
raise e
|
||||
try:
|
||||
ss = await bot.get_sticker_set(sticker_set_name)
|
||||
assert isinstance(ss, StickerSet)
|
||||
|
||||
if sticker_set.startswith(test_by):
|
||||
s = await bot.create_new_sticker_set(
|
||||
chat_id,
|
||||
name=sticker_set,
|
||||
title="Sticker Test",
|
||||
stickers=[
|
||||
InputSticker(
|
||||
sticker_file, emoji_list=["😄"], format=StickerFormat.STATIC
|
||||
)
|
||||
],
|
||||
if len(ss.stickers) > 100:
|
||||
try:
|
||||
await asyncio.gather(
|
||||
*[bot.delete_sticker_from_set(s.file_id) for s in ss.stickers[:-50]]
|
||||
)
|
||||
assert s
|
||||
elif sticker_set.startswith("animated"):
|
||||
a = await bot.create_new_sticker_set(
|
||||
chat_id,
|
||||
name=sticker_set,
|
||||
title="Animated Test",
|
||||
stickers=[
|
||||
InputSticker(
|
||||
animated_sticker_file,
|
||||
emoji_list=["😄"],
|
||||
format=StickerFormat.ANIMATED,
|
||||
)
|
||||
],
|
||||
)
|
||||
assert a
|
||||
elif sticker_set.startswith("video"):
|
||||
v = await bot.create_new_sticker_set(
|
||||
chat_id,
|
||||
name=sticker_set,
|
||||
title="Video Test",
|
||||
stickers=[
|
||||
InputSticker(
|
||||
video_sticker_file, emoji_list=["😄"], format=StickerFormat.VIDEO
|
||||
)
|
||||
],
|
||||
)
|
||||
assert v
|
||||
except BadRequest as e:
|
||||
if e.message != "Stickerset_not_modified":
|
||||
raise Exception("stickerset is growing too large.") from None
|
||||
except BadRequest as e:
|
||||
if e.message != "Stickerset_invalid":
|
||||
raise e
|
||||
|
||||
async def test_delete_sticker_set(self, bot, chat_id, sticker_file):
|
||||
await asyncio.sleep(1) # Avoid floods
|
||||
s = await bot.create_new_sticker_set(
|
||||
chat_id,
|
||||
name=sticker_set_name,
|
||||
title="Mixed Sticker Tests",
|
||||
stickers=[
|
||||
InputSticker(
|
||||
data_file("telegram.webp").open("rb"),
|
||||
emoji_list=["😄"],
|
||||
format=StickerFormat.STATIC,
|
||||
),
|
||||
InputSticker(
|
||||
data_file("telegram_animated_sticker.tgs").open("rb"),
|
||||
emoji_list=["😄", "⭐"],
|
||||
format=StickerFormat.ANIMATED,
|
||||
),
|
||||
# works:
|
||||
InputSticker(
|
||||
data_file("telegram_video_sticker.webm").open("rb"),
|
||||
emoji_list=["😄", "🔥"],
|
||||
format=StickerFormat.VIDEO,
|
||||
),
|
||||
],
|
||||
sticker_type="regular",
|
||||
)
|
||||
assert s
|
||||
await asyncio.sleep(2) # Avoid floods
|
||||
|
||||
TestStickerSetWithRequest._WORKER_CREATED_STICKER_SET = True
|
||||
|
||||
@pytest.fixture
|
||||
def sticker_set_name(self, bot, worker_id):
|
||||
"""Returns the name of this worker's own sticker set."""
|
||||
safe_worker_id = worker_id.replace("-", "_")
|
||||
return f"{safe_worker_id}_test_by_{bot.username}"
|
||||
|
||||
@pytest.fixture
|
||||
async def worker_sticker_set(self, bot, sticker_set_name):
|
||||
"""Fetches and returns this worker's own sticker set."""
|
||||
return await bot.get_sticker_set(sticker_set_name)
|
||||
|
||||
async def test_delete_sticker_set(self, bot, chat_id, sticker_file, sticker_set_name):
|
||||
# there is currently an issue in the API where this function claims it successfully
|
||||
# creates an already deleted sticker set while it does not. This happens when calling it
|
||||
# too soon after deleting the set. This then leads to delete_sticker_set failing since the
|
||||
@@ -834,9 +857,10 @@ class TestStickerSetWithRequest:
|
||||
assert await bot.delete_sticker_set(name)
|
||||
|
||||
async def test_set_custom_emoji_sticker_set_thumbnail(
|
||||
self, bot, chat_id, animated_sticker_file
|
||||
self, bot, chat_id, animated_sticker_file, worker_id
|
||||
):
|
||||
ss_name = f"custom_emoji_set_by_{bot.username}"
|
||||
safe_worker_id = worker_id.replace("-", "_")
|
||||
ss_name = f"{safe_worker_id}_custom_emoji_set_by_{bot.username}"
|
||||
try:
|
||||
ss = await bot.get_sticker_set(ss_name)
|
||||
assert ss.sticker_type == Sticker.CUSTOM_EMOJI
|
||||
@@ -854,8 +878,15 @@ class TestStickerSetWithRequest:
|
||||
)
|
||||
assert await bot.set_custom_emoji_sticker_set_thumbnail(ss_name, "")
|
||||
|
||||
# Test add_sticker_to_set
|
||||
async def test_bot_methods_1_png(self, bot, chat_id, sticker_file):
|
||||
async def test_add_sticker_to_set(
|
||||
self,
|
||||
bot,
|
||||
chat_id,
|
||||
sticker_file,
|
||||
video_sticker_file,
|
||||
animated_sticker_file,
|
||||
sticker_set_name,
|
||||
):
|
||||
with data_file("telegram_sticker.png").open("rb") as f:
|
||||
# chat_id was hardcoded as 95205500 but it stopped working for some reason
|
||||
file = await bot.upload_sticker_file(
|
||||
@@ -864,87 +895,65 @@ class TestStickerSetWithRequest:
|
||||
assert file
|
||||
|
||||
await asyncio.sleep(1)
|
||||
tasks = asyncio.gather(
|
||||
bot.add_sticker_to_set(
|
||||
chat_id,
|
||||
f"test_by_{bot.username}",
|
||||
sticker=InputSticker(
|
||||
sticker=file.file_id, emoji_list=["😄"], format=StickerFormat.STATIC
|
||||
),
|
||||
),
|
||||
bot.add_sticker_to_set( # Also test with file input and mask
|
||||
chat_id,
|
||||
f"test_by_{bot.username}",
|
||||
sticker=InputSticker(
|
||||
sticker=sticker_file,
|
||||
emoji_list=["😄"],
|
||||
mask_position=MaskPosition(MaskPosition.EYES, -1, 1, 2),
|
||||
format=StickerFormat.STATIC,
|
||||
),
|
||||
|
||||
assert await bot.add_sticker_to_set(
|
||||
chat_id,
|
||||
sticker_set_name,
|
||||
sticker=InputSticker(
|
||||
sticker=file.file_id, emoji_list=["😄"], format=StickerFormat.STATIC
|
||||
),
|
||||
)
|
||||
await asyncio.sleep(1)
|
||||
assert await bot.add_sticker_to_set( # Also test with file input and mask
|
||||
chat_id,
|
||||
sticker_set_name,
|
||||
sticker=InputSticker(
|
||||
sticker=data_file("telegram.webp").open("rb"),
|
||||
emoji_list=["😄"],
|
||||
mask_position=MaskPosition(MaskPosition.EYES, -1, 1, 2),
|
||||
format=StickerFormat.STATIC,
|
||||
),
|
||||
)
|
||||
assert all(await tasks)
|
||||
|
||||
async def test_bot_methods_1_tgs(self, bot, chat_id):
|
||||
await asyncio.sleep(1)
|
||||
assert await bot.add_sticker_to_set(
|
||||
chat_id,
|
||||
f"animated_test_by_{bot.username}",
|
||||
sticker_set_name,
|
||||
sticker=InputSticker(
|
||||
sticker=data_file("telegram_animated_sticker.tgs").open("rb"),
|
||||
emoji_list=["😄"],
|
||||
format=StickerFormat.ANIMATED,
|
||||
),
|
||||
)
|
||||
|
||||
async def test_bot_methods_1_webm(self, bot, chat_id):
|
||||
await asyncio.sleep(1)
|
||||
with data_file("telegram_video_sticker.webm").open("rb") as f:
|
||||
assert await bot.add_sticker_to_set(
|
||||
chat_id,
|
||||
f"video_test_by_{bot.username}",
|
||||
sticker=InputSticker(sticker=f, emoji_list=["🤔"], format=StickerFormat.VIDEO),
|
||||
)
|
||||
|
||||
# Test set_sticker_position_in_set
|
||||
async def test_bot_methods_2_png(self, bot, sticker_set):
|
||||
await asyncio.sleep(1)
|
||||
file_id = sticker_set.stickers[0].file_id
|
||||
assert await bot.set_sticker_position_in_set(file_id, 1)
|
||||
|
||||
async def test_bot_methods_2_tgs(self, bot, animated_sticker_set):
|
||||
await asyncio.sleep(1)
|
||||
file_id = animated_sticker_set.stickers[0].file_id
|
||||
assert await bot.set_sticker_position_in_set(file_id, 1)
|
||||
|
||||
async def test_bot_methods_2_webm(self, bot, video_sticker_set):
|
||||
await asyncio.sleep(1)
|
||||
file_id = video_sticker_set.stickers[0].file_id
|
||||
assert await bot.set_sticker_position_in_set(file_id, 1)
|
||||
|
||||
# Test set_sticker_set_thumb
|
||||
async def test_bot_methods_3_png(self, bot, chat_id, sticker_set_thumb_file):
|
||||
await asyncio.sleep(1)
|
||||
assert await bot.set_sticker_set_thumbnail(
|
||||
f"test_by_{bot.username}", chat_id, format="static", thumbnail=sticker_set_thumb_file
|
||||
assert await bot.add_sticker_to_set(
|
||||
chat_id,
|
||||
sticker_set_name,
|
||||
sticker=InputSticker(
|
||||
sticker=data_file("telegram_video_sticker.webm").open("rb"),
|
||||
emoji_list=["🤔"],
|
||||
format=StickerFormat.VIDEO,
|
||||
),
|
||||
)
|
||||
|
||||
async def test_bot_methods_3_tgs(
|
||||
self, bot, chat_id, animated_sticker_file, animated_sticker_set
|
||||
async def test_sticker_position_in_set(self, bot, worker_sticker_set):
|
||||
await asyncio.sleep(1)
|
||||
file_id = worker_sticker_set.stickers[0].file_id
|
||||
assert await bot.set_sticker_position_in_set(file_id, 1)
|
||||
|
||||
async def test_sticker_set_thumbnail(
|
||||
self, bot, chat_id, sticker_set_thumb_file, animated_sticker_file, sticker_set_name
|
||||
):
|
||||
await asyncio.sleep(1)
|
||||
animated_test = f"animated_test_by_{bot.username}"
|
||||
file_id = animated_sticker_set.stickers[-1].file_id
|
||||
tasks = asyncio.gather(
|
||||
bot.set_sticker_set_thumbnail(
|
||||
animated_test,
|
||||
chat_id,
|
||||
"animated",
|
||||
thumbnail=animated_sticker_file,
|
||||
),
|
||||
bot.set_sticker_set_thumbnail(animated_test, chat_id, "animated", thumbnail=file_id),
|
||||
assert await bot.set_sticker_set_thumbnail(
|
||||
sticker_set_name, chat_id, format="static", thumbnail=sticker_set_thumb_file
|
||||
)
|
||||
await asyncio.sleep(1)
|
||||
assert await bot.set_sticker_set_thumbnail(
|
||||
sticker_set_name,
|
||||
chat_id,
|
||||
"animated",
|
||||
thumbnail=animated_sticker_file,
|
||||
)
|
||||
assert all(await tasks)
|
||||
|
||||
# TODO: Try the below by creating a custom .webm and not by downloading another pack's thumb
|
||||
@pytest.mark.skip(
|
||||
@@ -954,85 +963,41 @@ class TestStickerSetWithRequest:
|
||||
def test_bot_methods_3_webm(self, bot, chat_id, video_sticker_file, video_sticker_set):
|
||||
pass
|
||||
|
||||
# Test delete_sticker_from_set
|
||||
async def test_bot_methods_4_png(self, bot, sticker_set):
|
||||
if len(sticker_set.stickers) <= 1:
|
||||
async def test_delete_sticker_from_set(self, bot, worker_sticker_set):
|
||||
if len(worker_sticker_set.stickers) <= 1:
|
||||
pytest.skip("Sticker set only has one sticker, deleting it will delete the set.")
|
||||
await asyncio.sleep(1)
|
||||
file_id = sticker_set.stickers[-1].file_id
|
||||
file_id = worker_sticker_set.stickers[-1].file_id
|
||||
assert await bot.delete_sticker_from_set(file_id)
|
||||
|
||||
async def test_bot_methods_4_tgs(self, bot, animated_sticker_set):
|
||||
if len(animated_sticker_set.stickers) <= 1:
|
||||
pytest.skip("Sticker set only has one sticker, deleting it will delete the set.")
|
||||
await asyncio.sleep(1)
|
||||
file_id = animated_sticker_set.stickers[-1].file_id
|
||||
assert await bot.delete_sticker_from_set(file_id)
|
||||
|
||||
async def test_bot_methods_4_webm(self, bot, video_sticker_set):
|
||||
if len(video_sticker_set.stickers) <= 1:
|
||||
pytest.skip("Sticker set only has one sticker, deleting it will delete the set.")
|
||||
await asyncio.sleep(1)
|
||||
file_id = video_sticker_set.stickers[-1].file_id
|
||||
assert await bot.delete_sticker_from_set(file_id)
|
||||
|
||||
# Test set_sticker_emoji_list. It has been found that the first emoji in the list is the one
|
||||
# It has been found that the first emoji in the list is the one
|
||||
# that is used in `Sticker.emoji` as string (which is returned in `get_sticker_set`)
|
||||
async def test_bot_methods_5_png(self, bot, sticker_set):
|
||||
file_id = sticker_set.stickers[-1].file_id
|
||||
async def test_set_sticker_emoji_list(self, bot, worker_sticker_set, sticker_set_name):
|
||||
file_id = worker_sticker_set.stickers[-1].file_id
|
||||
assert await bot.set_sticker_emoji_list(file_id, ["😔", "😟"])
|
||||
ss = await bot.get_sticker_set(f"test_by_{bot.username}")
|
||||
ss = await bot.get_sticker_set(sticker_set_name)
|
||||
assert ss.stickers[-1].emoji == "😔"
|
||||
|
||||
async def test_bot_methods_5_tgs(self, bot, animated_sticker_set):
|
||||
file_id = animated_sticker_set.stickers[-1].file_id
|
||||
assert await bot.set_sticker_emoji_list(file_id, ["😔", "😟"])
|
||||
ss = await bot.get_sticker_set(f"animated_test_by_{bot.username}")
|
||||
assert ss.stickers[-1].emoji == "😔"
|
||||
|
||||
async def test_bot_methods_5_webm(self, bot, video_sticker_set):
|
||||
file_id = video_sticker_set.stickers[-1].file_id
|
||||
assert await bot.set_sticker_emoji_list(file_id, ["😔", "😟"])
|
||||
ss = await bot.get_sticker_set(f"video_test_by_{bot.username}")
|
||||
assert ss.stickers[-1].emoji == "😔"
|
||||
|
||||
# Test set_sticker_set_title.
|
||||
async def test_bot_methods_6_png(self, bot):
|
||||
assert await bot.set_sticker_set_title(f"test_by_{bot.username}", "new title")
|
||||
ss = await bot.get_sticker_set(f"test_by_{bot.username}")
|
||||
async def test_sticker_set_title(self, bot, sticker_set_name):
|
||||
assert await bot.set_sticker_set_title(sticker_set_name, "new title")
|
||||
ss = await bot.get_sticker_set(sticker_set_name)
|
||||
assert ss.title == "new title"
|
||||
|
||||
async def test_bot_methods_6_tgs(self, bot):
|
||||
assert await bot.set_sticker_set_title(f"animated_test_by_{bot.username}", "new title")
|
||||
ss = await bot.get_sticker_set(f"animated_test_by_{bot.username}")
|
||||
assert ss.title == "new title"
|
||||
|
||||
async def test_bot_methods_6_webm(self, bot):
|
||||
assert await bot.set_sticker_set_title(f"video_test_by_{bot.username}", "new title")
|
||||
ss = await bot.get_sticker_set(f"video_test_by_{bot.username}")
|
||||
assert ss.title == "new title"
|
||||
|
||||
# Test set_sticker_keywords. No way to find out the set keywords on a sticker after setting it.
|
||||
async def test_bot_methods_7_png(self, bot, sticker_set):
|
||||
file_id = sticker_set.stickers[-1].file_id
|
||||
# No way to find out the set keywords on a sticker after setting it.
|
||||
async def test_set_sticker_keywords(self, bot, worker_sticker_set):
|
||||
file_id = worker_sticker_set.stickers[-1].file_id
|
||||
assert await bot.set_sticker_keywords(file_id, ["test", "test2"])
|
||||
|
||||
async def test_bot_methods_7_tgs(self, bot, animated_sticker_set):
|
||||
file_id = animated_sticker_set.stickers[-1].file_id
|
||||
assert await bot.set_sticker_keywords(file_id, ["test", "test2"])
|
||||
|
||||
async def test_bot_methods_7_webm(self, bot, video_sticker_set):
|
||||
file_id = video_sticker_set.stickers[-1].file_id
|
||||
assert await bot.set_sticker_keywords(file_id, ["test", "test2"])
|
||||
|
||||
async def test_bot_methods_8_png(self, bot, sticker_set, sticker_file):
|
||||
file_id = sticker_set.stickers[-1].file_id
|
||||
async def test_replace_sticker_in_set(
|
||||
self, bot, worker_sticker_set, sticker_file, sticker_set_name
|
||||
):
|
||||
file_id = worker_sticker_set.stickers[-1].file_id
|
||||
assert await bot.replace_sticker_in_set(
|
||||
bot.id,
|
||||
f"test_by_{bot.username}",
|
||||
sticker_set_name,
|
||||
file_id,
|
||||
sticker=InputSticker(
|
||||
sticker=sticker_file,
|
||||
sticker=data_file("telegram.webp").open("rb"),
|
||||
emoji_list=["😄"],
|
||||
format=StickerFormat.STATIC,
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user