소스 검색

🐛 fix(dialog): fix the behavior when in groups only one user can press the buttons

it was done by hacking aiogram middleware globaly
metya 3 년 전
부모
커밋
24525a5945
5개의 변경된 파일132개의 추가작업 그리고 33개의 파일을 삭제
  1. 35 0
      conda/develop.yaml
  2. 2 2
      db/papers.db
  3. 20 4
      src/dialog.py
  4. 58 10
      src/progress.py
  5. 17 17
      vanitybot.py

+ 35 - 0
conda/develop.yaml

@@ -12,6 +12,7 @@ dependencies:
   - asttokens=2.2.1=pyhd8ed1ab_0
   - asttokens=2.2.1=pyhd8ed1ab_0
   - async-timeout=4.0.2=pyhd8ed1ab_0
   - async-timeout=4.0.2=pyhd8ed1ab_0
   - async_generator=1.10=py_0
   - async_generator=1.10=py_0
+  - atk-1.0=2.38.0=hcb7b3dd_1
   - attrs=22.2.0=pyh71513ae_0
   - attrs=22.2.0=pyh71513ae_0
   - backcall=0.2.0=pyh9f0ad1d_0
   - backcall=0.2.0=pyh9f0ad1d_0
   - backports=1.0=pyhd8ed1ab_3
   - backports=1.0=pyhd8ed1ab_3
@@ -20,6 +21,7 @@ dependencies:
   - brotlipy=0.7.0=py311he2be06e_1005
   - brotlipy=0.7.0=py311he2be06e_1005
   - bzip2=1.0.8=h3422bc3_4
   - bzip2=1.0.8=h3422bc3_4
   - ca-certificates=2022.12.7=h4653dfc_0
   - ca-certificates=2022.12.7=h4653dfc_0
+  - cairo=1.16.0=h73a0509_1014
   - cchardet=2.1.7=py311ha397e9f_4
   - cchardet=2.1.7=py311ha397e9f_4
   - certifi=2022.12.7=pyhd8ed1ab_0
   - certifi=2022.12.7=pyhd8ed1ab_0
   - cffi=1.15.1=py311hae827db_3
   - cffi=1.15.1=py311hae827db_3
@@ -34,15 +36,34 @@ dependencies:
   - debugpy=1.6.6=py311ha397e9f_0
   - debugpy=1.6.6=py311ha397e9f_0
   - decli=0.5.2=pyhd8ed1ab_0
   - decli=0.5.2=pyhd8ed1ab_0
   - decorator=5.1.1=pyhd8ed1ab_0
   - decorator=5.1.1=pyhd8ed1ab_0
+  - deprecated=1.2.13=pyh6c4a22f_0
+  - diagrams=0.21.0=pyhd8ed1ab_0
   - distlib=0.3.6=pyhd8ed1ab_0
   - distlib=0.3.6=pyhd8ed1ab_0
   - docopt=0.6.2=py_1
   - docopt=0.6.2=py_1
   - executing=1.2.0=pyhd8ed1ab_0
   - executing=1.2.0=pyhd8ed1ab_0
+  - expat=2.5.0=hb7217d7_0
   - fake-useragent=1.1.1=pyhd8ed1ab_0
   - fake-useragent=1.1.1=pyhd8ed1ab_0
   - filelock=3.9.0=pyhd8ed1ab_0
   - filelock=3.9.0=pyhd8ed1ab_0
+  - font-ttf-dejavu-sans-mono=2.37=hab24e00_0
+  - font-ttf-inconsolata=3.000=h77eed37_0
+  - font-ttf-source-code-pro=2.038=h77eed37_0
+  - font-ttf-ubuntu=0.83=hab24e00_0
+  - fontconfig=2.14.2=h82840c6_0
+  - fonts-conda-ecosystem=1=0
+  - fonts-conda-forge=1=0
   - freetype=2.12.1=hd633e50_1
   - freetype=2.12.1=hd633e50_1
+  - fribidi=1.0.10=h27ca646_0
   - frozenlist=1.3.3=py311he2be06e_0
   - frozenlist=1.3.3=py311he2be06e_0
+  - gdk-pixbuf=2.42.10=h9bcf4fe_0
+  - gettext=0.21.1=h0186832_0
+  - giflib=5.2.1=h27ca646_2
+  - graphite2=1.3.13=h9f76cd9_1001
+  - graphviz=7.1.0=h4f8fbd6_0
   - greenlet=2.0.2=py311ha397e9f_0
   - greenlet=2.0.2=py311ha397e9f_0
+  - gtk2=2.24.33=h57013de_2
+  - gts=0.7.6=h4b6d4d6_2
   - h11=0.14.0=pyhd8ed1ab_0
   - h11=0.14.0=pyhd8ed1ab_0
+  - harfbuzz=6.0.0=hddbc195_0
   - icu=70.1=h6b3803e_0
   - icu=70.1=h6b3803e_0
   - identify=2.5.17=pyhd8ed1ab_0
   - identify=2.5.17=pyhd8ed1ab_0
   - idna=3.4=pyhd8ed1ab_0
   - idna=3.4=pyhd8ed1ab_0
@@ -60,12 +81,17 @@ dependencies:
   - libcxx=14.0.6=h2692d47_0
   - libcxx=14.0.6=h2692d47_0
   - libdeflate=1.17=h1a8c8d9_0
   - libdeflate=1.17=h1a8c8d9_0
   - libffi=3.4.2=h3422bc3_5
   - libffi=3.4.2=h3422bc3_5
+  - libgd=2.3.3=h90fb8ed_4
+  - libglib=2.74.1=h4646484_1
   - libiconv=1.17=he4db4b2_0
   - libiconv=1.17=he4db4b2_0
   - libpng=1.6.39=h76d750c_0
   - libpng=1.6.39=h76d750c_0
+  - librsvg=2.54.4=ha2634a2_0
   - libsodium=1.0.18=h27ca646_1
   - libsodium=1.0.18=h27ca646_1
   - libsqlite=3.40.0=h76d750c_0
   - libsqlite=3.40.0=h76d750c_0
   - libtiff=4.5.0=h5dffbdd_2
   - libtiff=4.5.0=h5dffbdd_2
+  - libtool=2.4.7=hb7217d7_0
   - libuv=1.44.2=he4db4b2_0
   - libuv=1.44.2=he4db4b2_0
+  - libwebp=1.2.4=h999c80f_1
   - libwebp-base=1.2.4=h57fd34a_0
   - libwebp-base=1.2.4=h57fd34a_0
   - libxcb=1.13=h9b22ae9_1004
   - libxcb=1.13=h9b22ae9_1004
   - libxml2=2.10.3=h87b0503_0
   - libxml2=2.10.3=h87b0503_0
@@ -84,13 +110,16 @@ dependencies:
   - openssl=3.0.8=h03a7124_0
   - openssl=3.0.8=h03a7124_0
   - outcome=1.2.0=pyhd8ed1ab_0
   - outcome=1.2.0=pyhd8ed1ab_0
   - packaging=21.3=pyhd8ed1ab_0
   - packaging=21.3=pyhd8ed1ab_0
+  - pango=1.50.13=h6c112b8_0
   - parse=1.19.0=pyh44b312d_0
   - parse=1.19.0=pyh44b312d_0
   - parso=0.8.3=pyhd8ed1ab_0
   - parso=0.8.3=pyhd8ed1ab_0
+  - pcre2=10.40=hb34f9b4_0
   - pexpect=4.8.0=pyh1a96a4e_2
   - pexpect=4.8.0=pyh1a96a4e_2
   - pickleshare=0.7.5=py_1003
   - pickleshare=0.7.5=py_1003
   - pillow=9.4.0=py311h627eb56_1
   - pillow=9.4.0=py311h627eb56_1
   - pip=23.0=pyhd8ed1ab_0
   - pip=23.0=pyhd8ed1ab_0
   - pipreqs=0.4.11=pyhd8ed1ab_0
   - pipreqs=0.4.11=pyhd8ed1ab_0
+  - pixman=0.40.0=h27ca646_0
   - platformdirs=2.6.2=pyhd8ed1ab_0
   - platformdirs=2.6.2=pyhd8ed1ab_0
   - pre-commit=3.0.4=py311h267d04e_0
   - pre-commit=3.0.4=py311h267d04e_0
   - prompt-toolkit=3.0.36=pyha770c72_0
   - prompt-toolkit=3.0.36=pyha770c72_0
@@ -103,7 +132,10 @@ dependencies:
   - pycparser=2.21=pyhd8ed1ab_0
   - pycparser=2.21=pyhd8ed1ab_0
   - pydantic=1.10.4=py311he2be06e_1
   - pydantic=1.10.4=py311he2be06e_1
   - pyee=8.1.0=pyhd8ed1ab_0
   - pyee=8.1.0=pyhd8ed1ab_0
+  - pygithub=1.58.0=pyh1a96a4e_0
   - pygments=2.14.0=pyhd8ed1ab_0
   - pygments=2.14.0=pyhd8ed1ab_0
+  - pyjwt=2.6.0=pyhd8ed1ab_0
+  - pynacl=1.5.0=py311he2be06e_2
   - pyopenssl=23.0.0=pyhd8ed1ab_0
   - pyopenssl=23.0.0=pyhd8ed1ab_0
   - pyparsing=3.0.9=pyhd8ed1ab_0
   - pyparsing=3.0.9=pyhd8ed1ab_0
   - pyppeteer=1.0.2=pyhd8ed1ab_0
   - pyppeteer=1.0.2=pyhd8ed1ab_0
@@ -112,6 +144,7 @@ dependencies:
   - python=3.11.0=h3ba56d0_1_cpython
   - python=3.11.0=h3ba56d0_1_cpython
   - python-dateutil=2.8.2=pyhd8ed1ab_0
   - python-dateutil=2.8.2=pyhd8ed1ab_0
   - python-dotenv=0.21.1=pyhd8ed1ab_0
   - python-dotenv=0.21.1=pyhd8ed1ab_0
+  - python-graphviz=0.19.2=pyhaef67bd_0
   - python_abi=3.11=3_cp311
   - python_abi=3.11=3_cp311
   - pyyaml=6.0=py311he2be06e_5
   - pyyaml=6.0=py311he2be06e_5
   - pyzmq=25.0.0=py311h0f351f6_0
   - pyzmq=25.0.0=py311h0f351f6_0
@@ -151,6 +184,7 @@ dependencies:
   - webdriver-manager=3.8.5=pyhd8ed1ab_0
   - webdriver-manager=3.8.5=pyhd8ed1ab_0
   - websockets=10.4=py311he2be06e_1
   - websockets=10.4=py311he2be06e_1
   - wheel=0.38.4=pyhd8ed1ab_0
   - wheel=0.38.4=pyhd8ed1ab_0
+  - wrapt=1.15.0=py311he2be06e_0
   - wsproto=1.2.0=pyhd8ed1ab_0
   - wsproto=1.2.0=pyhd8ed1ab_0
   - xorg-libxau=1.0.9=h27ca646_0
   - xorg-libxau=1.0.9=h27ca646_0
   - xorg-libxdmcp=1.1.3=h27ca646_0
   - xorg-libxdmcp=1.1.3=h27ca646_0
@@ -160,6 +194,7 @@ dependencies:
   - yarl=1.8.2=py311he2be06e_0
   - yarl=1.8.2=py311he2be06e_0
   - zeromq=4.3.4=hbdafb3b_1
   - zeromq=4.3.4=hbdafb3b_1
   - zipp=3.12.1=pyhd8ed1ab_0
   - zipp=3.12.1=pyhd8ed1ab_0
+  - zlib=1.2.13=h03a7124_4
   - zstd=1.5.2=hf913c23_6
   - zstd=1.5.2=hf913c23_6
   - pip:
   - pip:
       - aiogram==2.25.1
       - aiogram==2.25.1

+ 2 - 2
db/papers.db

@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
 version https://git-lfs.github.com/spec/v1
-oid sha256:ed42d25abd51465439938b7a8997049d1c68b8ded5997b45624a6bec2d2e20e6
-size 110592
+oid sha256:1d012733f595fa372b051e4040b3f73a62ef1065839665fefa3d5441059563ee
+size 188416

+ 20 - 4
src/dialog.py

@@ -4,7 +4,7 @@ from asyncio import sleep, create_task, gather, get_event_loop, Queue, current_t
 from typing import Any, List
 from typing import Any, List
 from aiogram.dispatcher.filters.state import StatesGroup, State
 from aiogram.dispatcher.filters.state import StatesGroup, State
 from aiogram.types import Message, CallbackQuery
 from aiogram.types import Message, CallbackQuery
-from aiogram_dialog import Window, Dialog, DialogManager, StartMode, Data
+from aiogram_dialog import Window, Dialog, DialogManager, StartMode, Data, BaseDialogManager
 from aiogram_dialog.widgets.kbd import Radio
 from aiogram_dialog.widgets.kbd import Radio
 from aiogram_dialog.widgets.text import Format
 from aiogram_dialog.widgets.text import Format
 from aiogram.types import ParseMode
 from aiogram.types import ParseMode
@@ -33,6 +33,14 @@ buttons = [
     ]
     ]
 
 
 
 
+async def time_out_dialog(manager: BaseDialogManager, widget: Any, time_out: int = 1200):
+    for _ in range(time_out):
+        await sleep(1)
+    await manager.update({"final_state": "1"})
+    await sleep(1)
+    await manager.done()
+
+
 async def get_data(dialog_manager: DialogManager, **kwargs):
 async def get_data(dialog_manager: DialogManager, **kwargs):
     text_message = {"text": "OOOPS!"}
     text_message = {"text": "OOOPS!"}
     if data := dialog_manager.current_context():
     if data := dialog_manager.current_context():
@@ -44,6 +52,8 @@ async def get_data(dialog_manager: DialogManager, **kwargs):
         item_id = data.widget_data.get('radio_buttons') #type: ignore
         item_id = data.widget_data.get('radio_buttons') #type: ignore
         title = data.start_data["title"] #type: ignore
         title = data.start_data["title"] #type: ignore
         url = data.start_data["url"] #type: ignore
         url = data.start_data["url"] #type: ignore
+        if ft:=data.dialog_data.get("final_state"):
+            item_id = ft
         if data.dialog_data.get('abs'): #type: ignore
         if data.dialog_data.get('abs'): #type: ignore
             abstract = data.dialog_data.get('abs') #type: ignore
             abstract = data.dialog_data.get('abs') #type: ignore
         else:
         else:
@@ -117,6 +127,12 @@ async def on_button_selected(c: CallbackQuery, widget: Any, manager: DialogManag
     return {"text": item_id}
     return {"text": item_id}
 
 
 
 
+async def default_button(c: CallbackQuery, dialog_manager: DialogManager, **kwargs):
+    if widget:=dialog_manager.dialog().find("radio_buttons"):
+        await widget.set_checked(c, "1", dialog_manager)
+    create_task(time_out_dialog(manager=dialog_manager.bg(), widget=widget))
+
+
 buttons_kbd = Radio(
 buttons_kbd = Radio(
     Format("✓ {item[0]}"),
     Format("✓ {item[0]}"),
     Format("{item[0]}"),
     Format("{item[0]}"),
@@ -135,6 +151,6 @@ dialog = Dialog(
         getter=get_data,
         getter=get_data,
         parse_mode=ParseMode.MARKDOWN, # type: ignore
         parse_mode=ParseMode.MARKDOWN, # type: ignore
         # preview_data={"button": "1"}
         # preview_data={"button": "1"}
-    )
-)
-
+    ),
+    on_start=default_button,
+)

+ 58 - 10
src/progress.py

@@ -4,16 +4,35 @@ import logging
 from aiogram import Bot, Dispatcher
 from aiogram import Bot, Dispatcher
 from aiogram.contrib.fsm_storage.memory import MemoryStorage
 from aiogram.contrib.fsm_storage.memory import MemoryStorage
 from aiogram.dispatcher.filters.state import StatesGroup, State
 from aiogram.dispatcher.filters.state import StatesGroup, State
-from aiogram.types import Message, CallbackQuery
+from aiogram.dispatcher.middlewares import BaseMiddleware
+from aiogram.types import Message, CallbackQuery, User
+from aiogram.utils.exceptions import Throttled
+from aiogram.dispatcher import DEFAULT_RATE_LIMIT
+from aiogram.dispatcher.handler import CancelHandler, current_handler
 
 
 from aiogram_dialog import Dialog, DialogManager, Window, DialogRegistry, BaseDialogManager, \
 from aiogram_dialog import Dialog, DialogManager, Window, DialogRegistry, BaseDialogManager, \
     StartMode
     StartMode
 from aiogram_dialog.widgets.kbd import Button
 from aiogram_dialog.widgets.kbd import Button
-from aiogram_dialog.widgets.text import Const, Multi, Text
+from aiogram_dialog.widgets.text import Const, Multi, Text, Format
 
 
-from typing import Any
+from typing import Any, Callable, Dict, Awaitable, Union
 
 
-from src.config import API_TOKEN
+try: 
+    from src.config import API_TOKEN
+except:
+    from config import API_TOKEN
+
+class TrickyUser(BaseMiddleware):
+    def __init__(self, user_id: int,):
+        super().__init__()
+        self.default_user = user_id
+
+    async def on_process_update(self, update, data):
+        if message:=update.message:
+            message.from_user.id = self.default_user
+        if callback:=update.callback_query:
+            callback.from_user.id = self.default_user
+        # print(update)
 
 
 
 
 class Processing(Text):
 class Processing(Text):
@@ -60,12 +79,29 @@ bg_dialog = Dialog(
 # main dialog
 # main dialog
 class MainSG(StatesGroup):
 class MainSG(StatesGroup):
     main = State()
     main = State()
+    background = State()
+
+
+async def start_bg(c: CallbackQuery, button: Button, dialog_manager: DialogManager, **kwargs):
+    # await manager.start(Bg.progress)
+    # asyncio.create_task(background(c, manager.bg()))
+    await dialog_manager.dialog().switch_to(MainSG.background)
+    await task_background(dialog_manager)
 
 
 
 
-async def start_bg(c: CallbackQuery, button: Button, manager: DialogManager):
-    await manager.start(Bg.progress)
-    asyncio.create_task(background(c, manager.bg()))
+async def get_task_bg_data(dialog_manager: DialogManager, **kwargs):
+    for key, val in kwargs.items():
+        print(key, val)
+    kwargs['aiogd_storage_proxy'].user_id = 1
+    print(dialog_manager)
+    return {"text": dialog_manager.current_context().dialog_data.get("text", "gay")} # type: ignore
 
 
+async def task_background(manager: DialogManager):
+    print(manager.current_context())
+    for i in range(5):
+        print(i)
+        await manager.update({"text": f"pidor{i}"})
+        await asyncio.sleep(1)
 
 
 async def background(c: CallbackQuery,
 async def background(c: CallbackQuery,
                      manager: DialogManager | BaseDialogManager,
                      manager: DialogManager | BaseDialogManager,
@@ -92,17 +128,28 @@ async def background(c: CallbackQuery,
     await manager.done()
     await manager.done()
 
 
 
 
+async def back(c, button, dialog_manager):
+    await dialog_manager.dialog().back()
+
 main_menu = Dialog(
 main_menu = Dialog(
     Window(
     Window(
         Const("Press button to start processing"),
         Const("Press button to start processing"),
         Button(Const("Start 👀"), id="start", on_click=start_bg),
         Button(Const("Start 👀"), id="start", on_click=start_bg),
         state=MainSG.main,
         state=MainSG.main,
-        getter=get_bg_data
+        getter=get_task_bg_data
     ),
     ),
+    Window(
+        Const("THIS IS BACKGROUND"),
+        Format("{text}"),
+        Button(Const("Back"), id='back', on_click=back),
+        state=MainSG.background,
+        getter=get_task_bg_data
+    )
 )
 )
 
 
 
 
-async def start(m: Message, dialog_manager: DialogManager):
+async def start(m: Message, dialog_manager: DialogManager, **kwargs):
+    # print(kwargs)
     await dialog_manager.start(MainSG.main, mode=StartMode.RESET_STACK)
     await dialog_manager.start(MainSG.main, mode=StartMode.RESET_STACK)
 
 
 
 
@@ -116,8 +163,9 @@ async def main():
     registry = DialogRegistry(dp)
     registry = DialogRegistry(dp)
     registry.register(bg_dialog)
     registry.register(bg_dialog)
     registry.register(main_menu)
     registry.register(main_menu)
+    dp.middleware.setup(TrickyUser(1))
     dp.register_message_handler(start, text="/start", state="*")
     dp.register_message_handler(start, text="/start", state="*")
-
+    await dp.skip_updates()
     await dp.start_polling()
     await dp.start_polling()
 
 
 
 

+ 17 - 17
vanitybot.py

@@ -3,42 +3,36 @@ import logging
 import asyncio
 import asyncio
 from aiogram.contrib.fsm_storage.memory import MemoryStorage
 from aiogram.contrib.fsm_storage.memory import MemoryStorage
 from aiogram import Bot, Dispatcher, executor, types
 from aiogram import Bot, Dispatcher, executor, types
-from aiogram_dialog import DialogManager, DialogRegistry, StartMode
+from aiogram_dialog import DialogManager, DialogRegistry, StartMode, BaseDialogManager
 from src.config import API_TOKEN
 from src.config import API_TOKEN
 from src.dialog import dialog, MySG
 from src.dialog import dialog, MySG
-from src.progress import bg_dialog
+from src.progress import bg_dialog, TrickyUser
 from src.summarize import get_paper_desc
 from src.summarize import get_paper_desc
 
 
 # Initialize bot and dispatcher
 # Initialize bot and dispatcher
 bot = Bot(token=API_TOKEN) # type: ignore
 bot = Bot(token=API_TOKEN) # type: ignore
 dp = Dispatcher(bot, storage=MemoryStorage())
 dp = Dispatcher(bot, storage=MemoryStorage())
 registry = DialogRegistry(dp)
 registry = DialogRegistry(dp)
+dp.middleware.setup(TrickyUser(1))
 registry.register(dialog)
 registry.register(dialog)
 registry.register(bg_dialog)
 registry.register(bg_dialog)
 
 
+
 help_message = "Hello!\n\n\
 help_message = "Hello!\n\n\
 Send me a link paper from arxiv.org and \
 Send me a link paper from arxiv.org and \
 I'll send you back snippet of paper and arxiv-vanity.com mobile friendly link!\n\
 I'll send you back snippet of paper and arxiv-vanity.com mobile friendly link!\n\
 Or add me to chat and I'll be watching the arxiv link and \
 Or add me to chat and I'll be watching the arxiv link and \
-reply to them with fancy arxiv-vanity links."
+reply to them with fancy arxiv-vanity links.\n\
+Also, I can get summary and highlight of a paper for you."
+
 
 
 async def deploy_message():
 async def deploy_message():
     await bot.send_message(chat_id=1147194, text='The deployment has been performed')
     await bot.send_message(chat_id=1147194, text='The deployment has been performed')
 
 
-@dp.message_handler(commands=['start'])
-async def process_start_command(message: types.Message):
-    await message.reply(help_message)
-
 
 
-@dp.message_handler(commands=['help'])
-async def process_help_command(message: types.Message):
+@dp.message_handler(commands=['start', 'help'])
+async def process_start_command(message: types.Message):
     await message.reply(help_message)
     await message.reply(help_message)
-    
-@dp.message_handler(commands=['long'])
-async def long(message: types.Message):
-    import random
-    long = "".join(str(random.randint(1,10)) for _ in range(3700))
-    await message.reply(long)
 
 
 
 
 @dp.message_handler(regexp=r'arxiv.org\/(?:abs|pdf)\/\d{4}\.\d{5}')
 @dp.message_handler(regexp=r'arxiv.org\/(?:abs|pdf)\/\d{4}\.\d{5}')
@@ -77,10 +71,16 @@ async def vanitify(message: types.Message, dialog_manager: DialogManager):
     asyncio.gather(*[start_dialog(data=data) for data in list_data])
     asyncio.gather(*[start_dialog(data=data) for data in list_data])
 
 
 
 
+async def main():
+    await deploy_message()
+    await dp.skip_updates()
+    await dp.start_polling()
+
 if __name__ == "__main__":
 if __name__ == "__main__":
     logging.basicConfig(level=logging.INFO)
     logging.basicConfig(level=logging.INFO)
     # logging.getLogger("asyncio").setLevel(logging.DEBUG)
     # logging.getLogger("asyncio").setLevel(logging.DEBUG)
     # logging.getLogger("aiogram_dialog").setLevel(logging.DEBUG)
     # logging.getLogger("aiogram_dialog").setLevel(logging.DEBUG)
 
 
-    asyncio.get_event_loop().run_until_complete(deploy_message())
-    executor.start_polling(dp, skip_updates=True)
+    # asyncio.get_event_loop().run_until_complete(deploy_message())
+    # executor.start_polling(dp, skip_updates=True)
+    asyncio.run(main())