ButtonBot / tests /test_postlinks.py
github-actions[bot]
Automated sync from GitHub
2e79f82
Raw History Blame Contribute Delete
8.13 kB
"""Links to other posts of the same channel: found in all three places and pointed at the new message ids."""
from telethon import types
from telethon.helpers import add_surrogate
from app.postlinks import PostLinks, parse_ref, relink_message, resolve_chain
from app.tgutil import button_url
from .fakes import make_msg, markup, url_btn
def links(ids=None, username="One_Piece_inEnglish", cid=1234567):
return PostLinks(username=username, channel_id=cid, ids=dict(ids or {}))
def utf16(text, ent):
raw = text.encode("utf-16-le")
return raw[ent.offset * 2 : (ent.offset + ent.length) * 2].decode("utf-16-le")
# ------------------------------------------------------------------------------------------- parsing
def test_the_forms_of_a_post_link_are_recognised():
r = parse_ref("https://t.me/One_Piece_inEnglish/115")
assert (r.name, r.cid, r.msg_id) == ("one_piece_inenglish", None, 115)
assert "https://t.me/One_Piece_inEnglish/115"[r.start : r.end] == "115"
r = parse_ref("t.me/c/1234567/56?single")
assert (r.name, r.cid, r.msg_id) == (None, 1234567, 56)
assert parse_ref("http://telegram.me/somechan/7#x").msg_id == 7
assert parse_ref("https://t.me/s/somechan/7").msg_id == 7
assert parse_ref("https://telegram.dog/somechan/7/2").msg_id == 7 # only the first number is the post
r = parse_ref("tg://resolve?domain=somechan&post=9")
assert (r.name, r.msg_id) == ("somechan", 9)
r = parse_ref("tg://resolve?post=9&domain=somechan")
assert (r.name, r.msg_id) == ("somechan", 9)
r = parse_ref("tg://privatepost?channel=1234567&post=9")
assert (r.cid, r.msg_id) == (1234567, 9)
def test_links_that_are_not_post_links_are_not_touched():
for url in (
"https://t.me/somechan",
"https://t.me/somechan?start=ABC123",
"https://t.me/+AbCdEfGhIj",
"https://t.me/addstickers/pack",
"https://example.com/somechan/12",
"https://t.me/c/1234567",
"tg://resolve?domain=somechan",
"",
None,
):
assert parse_ref(url) is None, url
def test_only_links_into_this_channel_count():
pl = links({115: 400})
assert pl.ref_of("https://t.me/one_piece_inenglish/115") # a username is not case sensitive
assert pl.ref_of("https://t.me/c/1234567/115")
assert pl.ref_of("https://t.me/c/999/115") is None
assert pl.ref_of("https://t.me/another_channel/115") is None
private = PostLinks(username=None, channel_id=1234567, ids={115: 400})
assert private.ref_of("https://t.me/One_Piece_inEnglish/115") is None
assert private.remap_url("https://t.me/c/1234567/115") == "https://t.me/c/1234567/400"
def test_only_the_digits_of_the_post_id_change():
pl = links({115: 1042, 7: 8})
assert pl.remap_url("https://t.me/One_Piece_inEnglish/115") == "https://t.me/One_Piece_inEnglish/1042"
assert pl.remap_url("https://t.me/One_Piece_inEnglish/115?single") == "https://t.me/One_Piece_inEnglish/1042?single"
assert pl.remap_url("t.me/c/1234567/7?comment=7") == "t.me/c/1234567/8?comment=7"
assert pl.remap_url("tg://resolve?domain=One_Piece_inEnglish&post=115") == "tg://resolve?domain=One_Piece_inEnglish&post=1042"
assert pl.remap_url("tg://resolve?post=115&domain=One_Piece_inEnglish&x=1") == "tg://resolve?post=1042&domain=One_Piece_inEnglish&x=1"
assert pl.remap_url("tg://privatepost?channel=1234567&post=7") == "tg://privatepost?channel=1234567&post=8"
# a post nobody copied, another channel, a bot link: unchanged
assert pl.remap_url("https://t.me/One_Piece_inEnglish/116") == "https://t.me/One_Piece_inEnglish/116"
assert pl.remap_url("https://t.me/other/115") == "https://t.me/other/115"
assert pl.remap_url("https://t.me/goku?start=115") == "https://t.me/goku?start=115"
# --------------------------------------------------------------------------------------- the text
def test_typed_links_are_rewritten_and_the_formatting_after_them_moves_along():
text = "πŸ˜€ see https://t.me/One_Piece_inEnglish/9 and bold t.me/c/1234567/10?single end"
bold = types.MessageEntityBold(text.index("bold") + 0, 4)
bold.offset = len(text[: text.index("bold")].encode("utf-16-le")) // 2 # offsets count UTF-16 units, the emoji is 2
url_ent = types.MessageEntityUrl(len("πŸ˜€ see ".encode("utf-16-le")) // 2, len("https://t.me/One_Piece_inEnglish/9"))
pl = links({9: 1234, 10: 12})
new_text, ents, n_typed, n_hyper = pl.remap_text(text, [bold, url_ent])
assert new_text == "πŸ˜€ see https://t.me/One_Piece_inEnglish/1234 and bold t.me/c/1234567/12?single end"
assert (n_typed, n_hyper) == (2, 0)
b, u = ents
assert utf16(new_text, b) == "bold" # the bold span moved with its text
assert utf16(new_text, u) == "https://t.me/One_Piece_inEnglish/1234" # the link entity grew with the link
assert bold.offset != b.offset # the originals were not modified
def test_a_link_typed_inside_another_word_or_url_is_not_a_post_link():
pl = links({9: 99})
text = "x https://example.com/?u=t.me/One_Piece_inEnglish/9 and email@t.me/One_Piece_inEnglish/9"
assert pl.remap_text(text, [])[0] == text
def test_hyperlinks_are_changed_without_touching_their_text():
text = "Click here and there"
ents = [
types.MessageEntityTextUrl(0, 10, "https://t.me/One_Piece_inEnglish/115"),
types.MessageEntityTextUrl(11, 3, "https://t.me/another/115"),
types.MessageEntityBold(15, 5),
]
new_text, new_ents, n_typed, n_hyper = links({115: 2000}).remap_text(text, ents)
assert new_text == text and (n_typed, n_hyper) == (0, 1)
assert new_ents[0].url == "https://t.me/One_Piece_inEnglish/2000" and new_ents[1].url == "https://t.me/another/115"
assert ents[0].url.endswith("/115") # the message's own entity objects are left alone
def test_buttons_with_a_link_to_a_post_are_rebuilt_other_buttons_are_kept_as_they_are():
mk = markup([url_btn("Episode 1", "https://t.me/One_Piece_inEnglish/115"), url_btn("Bot", "https://t.me/goku?start=A")])
new, n = links({115: 300}).remap_markup(mk)
assert n == 1
assert [button_url(b) for b in new.rows[0].buttons] == ["https://t.me/One_Piece_inEnglish/300", "https://t.me/goku?start=A"]
assert [b.text for b in new.rows[0].buttons] == ["Episode 1", "Bot"]
same, n = links({999: 1}).remap_markup(mk)
assert n == 0 and same is mk # nothing to change: the very same keyboard comes back
assert links().remap_markup(None) == (None, 0)
def test_links_are_counted_even_before_the_new_ids_are_known():
text = "https://t.me/One_Piece_inEnglish/5 and https://t.me/other/5"
ents = [types.MessageEntityTextUrl(0, 5, "https://t.me/c/1234567/6")]
mk = markup([url_btn("x", "https://t.me/One_Piece_inEnglish/7")])
assert links().count(text, ents, mk) == 3
def test_a_whole_message_is_relinked_or_reported_unchanged():
msg = make_msg(
5, "Next: https://t.me/One_Piece_inEnglish/115", [types.MessageEntityBold(0, 4)],
markup([url_btn("Go", "https://t.me/One_Piece_inEnglish/116")]),
)
assert relink_message(msg, links({1: 2})) is None
rl = relink_message(msg, links({115: 900, 116: 901}))
assert rl.text == "Next: https://t.me/One_Piece_inEnglish/900" and (rl.n_typed, rl.n_hyper, rl.n_buttons, rl.links) == (1, 0, 1, 2)
assert rl.text_changed and rl.markup_changed
assert button_url(rl.markup.rows[0].buttons[0]).endswith("/901")
# running it again on the result changes nothing: the new ids are not old ids
again = make_msg(5, rl.text, rl.entities, rl.markup)
assert relink_message(again, links({115: 900, 116: 901})) is None
def test_posts_that_moved_twice_point_at_their_latest_copy():
assert resolve_chain({1: 5, 5: 9, 2: 6}) == {1: 9, 5: 9, 2: 6}
assert resolve_chain({}) == {}
def test_offsets_use_utf16_units():
text = "πŸ˜€πŸ˜€ https://t.me/One_Piece_inEnglish/3"
assert len(add_surrogate(text)) == 4 + 1 + len("https://t.me/One_Piece_inEnglish/3")
new_text, _, n, _ = links({3: 30}).remap_text(text, [])
assert new_text == "πŸ˜€πŸ˜€ https://t.me/One_Piece_inEnglish/30" and n == 1