Skip to content

Commit

Permalink
Merge pull request #447 from Tishka17/feature/link_preview
Browse files Browse the repository at this point in the history
Link preview
  • Loading branch information
Tishka17 authored Dec 16, 2024
2 parents a572eba + eacc7bc commit 8a30174
Show file tree
Hide file tree
Showing 17 changed files with 304 additions and 24 deletions.
5 changes: 1 addition & 4 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,10 @@
# import os
# import sys
# sys.path.insert(0, os.path.abspath("."))

import datetime

# -- Project information -----------------------------------------------------

project = "aiogram-dialog"
copyright = f"{datetime.date.today().year}, Tishka17"
copyright = "%Y, Tishka17"
author = "Tishka17"
master_doc = "index"

Expand Down
8 changes: 5 additions & 3 deletions docs/widgets/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ Widgets and Rendering
Base information
********************

Currently there are 4 kinds of widgets: :ref:`texts <text_widgets>`, :ref:`keyboards <keyboard_widgets>`,
:ref:`input <input_widgets>`, :ref:`media<media_widgets>` and you can create your own :ref:`widgets<custom_widgets>`.
Currently there are 5 kinds of widgets: :ref:`texts <text_widgets>`, :ref:`keyboards <keyboard_widgets>`,
:ref:`input <input_widgets>`, :ref:`media<media_widgets>`, :ref:`link preview<link_preview>` and you can create your own :ref:`widgets<custom_widgets>`.

* **Texts** used to render text anywhere in dialog. It can be message text, button title and so on.
* **Keyboards** represent parts of ``InlineKeyboard``
* **Media** represent media attachment to message
* **Input** allows to process incoming messages from user. Is has no representation.
* **Link Preview** used to manage link previews in messages.

Widgets can display static (e.g. ``Const``) and dynamic (e.g. ``Format``) content. To use dynamic data you have to set it. See :ref:`passing data <passing_data>`.

Expand All @@ -37,5 +38,6 @@ Also there are 2 general types:
keyboard/index
input/index
media/index
link_preview/index
hiding/index
custom_widgets/index
custom_widgets/index
27 changes: 27 additions & 0 deletions docs/widgets/link_preview/example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from aiogram.filters.state import State, StatesGroup

from aiogram_dialog import Window
from aiogram_dialog.widgets.link_preview import LinkPreview
from aiogram_dialog.widgets.text import Const


class SG(StatesGroup):
MAIN = State()
SECOND = State()


window = Window(
Const("https://nplus1.ru/news/2024/05/23/voyager-1-science-data"),
LinkPreview(is_disabled=True),
state=SG.MAIN,
)

second_window = Window(
Const("some text"),
LinkPreview(
url=Const("https://nplus1.ru/news/2024/05/23/voyager-1-science-data"),
prefer_small_media=True,
show_above_text=True,
),
state=SG.MAIN,
)
23 changes: 23 additions & 0 deletions docs/widgets/link_preview/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
.. _link_preview:

LinkPreview
*************

The **LinkPreview** widget is used to manage link previews in messages.

Parameters:

* ``url``: A ``TextWidget`` with URL to be used in the link preview. If not provided, the first URL found in the message will be used.
* ``is_disabled``: that controls whether the link preview is displayed. If ``True``, the preview will be disabled.
* ``prefer_small_media``: that controls if the media in the link preview should be displayed in a smaller size. Ignored if media size change is not supported.
* ``prefer_large_media``: that controls if the media in the link preview should be enlarged. Ignored if media size change is not supported.
* ``show_above_text``: that specifies whether the link preview should be displayed above the message text. If ``True``, link preview be displayed above the message text.


Code example:

.. literalinclude:: ./example.py

.. autoclass:: aiogram_dialog.widgets.link_preview.LinkPreview
:special-members: __init__
:members: render_link_preview, _render_link_preview
2 changes: 2 additions & 0 deletions example/loading.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
setup_dialogs,
)
from aiogram_dialog.widgets.kbd import Button
from aiogram_dialog.widgets.link_preview import LinkPreview
from aiogram_dialog.widgets.text import Const, Multi, Progress

API_TOKEN = os.getenv("BOT_TOKEN")
Expand Down Expand Up @@ -75,6 +76,7 @@ async def background(callback: CallbackQuery, manager: BaseDialogManager):
Window(
Const("Press button to start processing"),
Button(Const("Start"), id="start", on_click=start_bg),
LinkPreview(url=Const("http://ya.ru")),
state=MainSG.main,
),
)
Expand Down
2 changes: 2 additions & 0 deletions example/mega/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from bot_dialogs.calendar import calendar_dialog
from bot_dialogs.counter import counter_dialog
from bot_dialogs.layouts import layouts_dialog
from bot_dialogs.link_preview import link_preview_dialog
from bot_dialogs.main import main_dialog
from bot_dialogs.mutltiwidget import multiwidget_dialog
from bot_dialogs.reply_buttons import reply_kbd_dialog
Expand Down Expand Up @@ -68,6 +69,7 @@ async def on_unknown_intent(event: ErrorEvent, dialog_manager: DialogManager):
multiwidget_dialog,
switch_dialog,
reply_kbd_dialog,
link_preview_dialog,
)


Expand Down
83 changes: 83 additions & 0 deletions example/mega/bot_dialogs/link_preview.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
from aiogram_dialog import (
Dialog,
Window,
)
from aiogram_dialog.widgets.kbd import SwitchTo
from aiogram_dialog.widgets.link_preview import LinkPreview
from aiogram_dialog.widgets.text import Const, Format

from . import states
from .common import MAIN_MENU_BUTTON


async def links_getter(**_):
return {
"main": "https://en.wikipedia.org/wiki/HTML_element",
"photo": "https://en.wikipedia.org/wiki/Hyperlink",
}


LinkPreview_MAIN_MENU_BUTTON = SwitchTo(
text=Const("Back"), id="back", state=states.LinkPreview.MAIN,
)
COMMON_TEXT = Format(
"This is demo of different link preview options.\n"
"Link in text: {main}\n"
"Link in preview can be different\n\n"
"Current mode is:",
)

BACK = SwitchTo(Const("back"), "_back", states.LinkPreview.MAIN)

link_preview_dialog = Dialog(
Window(
COMMON_TEXT,
Format("Default"),
SwitchTo(
Const("disable"), "_disable", states.LinkPreview.IS_DISABLED,
),
SwitchTo(
Const("prefer small media"), "_prefer_small_media",
states.LinkPreview.SMALL_MEDIA,
),
SwitchTo(
Const("prefer large media"), "_prefer_large_media",
states.LinkPreview.LARGE_MEDIA,
),
SwitchTo(
Const("show above text"), "_show_above_text",
states.LinkPreview.SHOW_ABOVE_TEXT,
),
MAIN_MENU_BUTTON,
state=states.LinkPreview.MAIN,
),
Window(
COMMON_TEXT,
Const("is_disabled=True"),
LinkPreview(is_disabled=True),
LinkPreview_MAIN_MENU_BUTTON,
state=states.LinkPreview.IS_DISABLED,
),
Window(
COMMON_TEXT,
Const("prefer_small_media=True"),
LinkPreview(Format("{photo}"), prefer_small_media=True),
LinkPreview_MAIN_MENU_BUTTON,
state=states.LinkPreview.SMALL_MEDIA,
),
Window(
COMMON_TEXT,
Const("prefer_large_media=True"),
LinkPreview(Format("{photo}"), prefer_large_media=True),
LinkPreview_MAIN_MENU_BUTTON,
state=states.LinkPreview.LARGE_MEDIA,
),
Window(
COMMON_TEXT,
Const("show_above_text=True"),
LinkPreview(Format("{photo}"), show_above_text=True),
LinkPreview_MAIN_MENU_BUTTON,
state=states.LinkPreview.SHOW_ABOVE_TEXT,
),
getter=links_getter,
)
5 changes: 5 additions & 0 deletions example/mega/bot_dialogs/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@
id="switch",
state=states.Switch.MAIN,
),
Start(
text=Const("🔗 Link Preview"),
id="linkpreview",
state=states.LinkPreview.MAIN,
),
Start(
text=Const("⌨️ Reply keyboard"),
id="reply",
Expand Down
8 changes: 8 additions & 0 deletions example/mega/bot_dialogs/states.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,11 @@ class Switch(StatesGroup):
MAIN = State()
INPUT = State()
LAST = State()


class LinkPreview(StatesGroup):
MAIN = State()
IS_DISABLED = State()
SMALL_MEDIA = State()
LARGE_MEDIA = State()
SHOW_ABOVE_TEXT = State()
3 changes: 2 additions & 1 deletion src/aiogram_dialog/api/entities/new_message.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
Chat,
ForceReply,
InlineKeyboardMarkup,
LinkPreviewOptions,
ReplyKeyboardMarkup,
ReplyKeyboardRemove,
)
Expand Down Expand Up @@ -43,5 +44,5 @@ class NewMessage:
reply_markup: Optional[MarkupVariant] = None
parse_mode: Optional[str] = None
show_mode: ShowMode = ShowMode.AUTO
disable_web_page_preview: Optional[bool] = None
media: Optional[MediaAttachment] = None
link_preview_options: Optional[LinkPreviewOptions] = None
3 changes: 2 additions & 1 deletion src/aiogram_dialog/api/internal/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"CALLBACK_DATA_KEY", "CONTEXT_KEY", "EVENT_SIMULATED",
"STACK_KEY", "STORAGE_KEY",
"ButtonVariant", "DataGetter", "InputWidget", "KeyboardWidget",
"MediaWidget", "RawKeyboard", "TextWidget", "Widget",
"LinkPreviewWidget", "MediaWidget", "RawKeyboard", "TextWidget", "Widget",
"WindowProtocol",
]

Expand All @@ -24,6 +24,7 @@
DataGetter,
InputWidget,
KeyboardWidget,
LinkPreviewWidget,
MediaWidget,
RawKeyboard,
TextWidget,
Expand Down
11 changes: 11 additions & 0 deletions src/aiogram_dialog/api/internal/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
CallbackQuery,
InlineKeyboardButton,
KeyboardButton,
LinkPreviewOptions,
Message,
)

Expand Down Expand Up @@ -41,6 +42,16 @@ async def render_text(
raise NotImplementedError


@runtime_checkable
class LinkPreviewWidget(Widget, Protocol):
@abstractmethod
async def render_link_preview(
self, data: dict, manager: DialogManager,
) -> Optional[LinkPreviewOptions]:
"""Create link preview."""
raise NotImplementedError


ButtonVariant = Union[InlineKeyboardButton, KeyboardButton]
RawKeyboard = list[list[ButtonVariant]]

Expand Down
16 changes: 9 additions & 7 deletions src/aiogram_dialog/manager/message_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,10 +129,13 @@ def need_voice(self, new_message: NewMessage) -> bool:
def _message_changed(
self, new_message: NewMessage, old_message: OldMessage,
) -> bool:
if new_message.text != old_message.text:
return True
# we cannot actually compare reply keyboards
if new_message.reply_markup or old_message.has_reply_keyboard:
if (
(new_message.text != old_message.text) or
# we cannot actually compare reply keyboards
(new_message.reply_markup or old_message.has_reply_keyboard) or
# we do not know if link preview changed
new_message.link_preview_options
):
return True

if self.had_media(old_message) != self.need_media(new_message):
Expand Down Expand Up @@ -348,7 +351,7 @@ async def edit_text(
text=new_message.text,
reply_markup=new_message.reply_markup,
parse_mode=new_message.parse_mode,
disable_web_page_preview=new_message.disable_web_page_preview,
link_preview_options=new_message.link_preview_options,
)

async def edit_media(
Expand All @@ -363,7 +366,6 @@ async def edit_media(
caption=new_message.text,
reply_markup=new_message.reply_markup,
parse_mode=new_message.parse_mode,
disable_web_page_preview=new_message.disable_web_page_preview,
media=await self.get_media_source(new_message.media, bot),
**new_message.media.kwargs,
)
Expand Down Expand Up @@ -392,9 +394,9 @@ async def send_text(self, bot: Bot, new_message: NewMessage) -> Message:
text=new_message.text,
message_thread_id=new_message.thread_id,
business_connection_id=new_message.business_connection_id,
disable_web_page_preview=new_message.disable_web_page_preview,
reply_markup=new_message.reply_markup,
parse_mode=new_message.parse_mode,
link_preview_options=new_message.link_preview_options,
)

async def send_media(self, bot: Bot, new_message: NewMessage) -> Message:
Expand Down
3 changes: 3 additions & 0 deletions src/aiogram_dialog/widgets/link_preview/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
__all__ = ["LinkPreviewBase", "LinkPreview"]

from .base import LinkPreview, LinkPreviewBase
Loading

0 comments on commit 8a30174

Please sign in to comment.