core.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. """Responsible for web init and mandatory ops"""
  2. # Friendly Telegram (telegram userbot)
  3. # Copyright (C) 2018-2021 The Authors
  4. # This program is free software: you can redistribute it and/or modify
  5. # it under the terms of the GNU Affero General Public License as published by
  6. # the Free Software Foundation, either version 3 of the License, or
  7. # (at your option) any later version.
  8. # This program is distributed in the hope that it will be useful,
  9. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. # GNU Affero General Public License for more details.
  12. # You should have received a copy of the GNU Affero General Public License
  13. # along with this program. If not, see <https://www.gnu.org/licenses/>.
  14. # ©️ Dan Gazizullin, 2021-2023
  15. # This file is a part of Hikka Userbot
  16. # 🌐 https://github.com/hikariatama/Hikka
  17. # You can redistribute it and/or modify it under the terms of the GNU AGPLv3
  18. # 🔑 https://www.gnu.org/licenses/agpl-3.0.html
  19. import asyncio
  20. import contextlib
  21. import inspect
  22. import logging
  23. import os
  24. import subprocess
  25. import aiohttp_jinja2
  26. import jinja2
  27. from aiohttp import web
  28. from ..database import Database
  29. from ..loader import Modules
  30. from ..tl_cache import CustomTelegramClient
  31. from . import proxypass, root
  32. logger = logging.getLogger(__name__)
  33. class Web(root.Web):
  34. def __init__(self, **kwargs):
  35. self.runner = None
  36. self.port = None
  37. self.running = asyncio.Event()
  38. self.ready = asyncio.Event()
  39. self.client_data = {}
  40. self.app = web.Application()
  41. self.proxypasser = proxypass.ProxyPasser()
  42. aiohttp_jinja2.setup(
  43. self.app,
  44. filters={"getdoc": inspect.getdoc, "ascii": ascii},
  45. loader=jinja2.FileSystemLoader("web-resources"),
  46. )
  47. self.app["static_root_url"] = "/static"
  48. super().__init__(**kwargs)
  49. self.app.router.add_get("/favicon.ico", self.favicon)
  50. self.app.router.add_static("/static/", "web-resources/static")
  51. async def start_if_ready(
  52. self,
  53. total_count: int,
  54. port: int,
  55. proxy_pass: bool = False,
  56. ):
  57. if total_count <= len(self.client_data):
  58. if not self.running.is_set():
  59. await self.start(port, proxy_pass=proxy_pass)
  60. self.ready.set()
  61. async def get_url(self, proxy_pass: bool) -> str:
  62. url = None
  63. if all(option in os.environ for option in {"LAVHOST", "USER", "SERVER"}):
  64. return f"https://{os.environ['USER']}.{os.environ['SERVER']}.lavhost.ml"
  65. if proxy_pass:
  66. with contextlib.suppress(Exception):
  67. url = await asyncio.wait_for(
  68. self.proxypasser.get_url(self.port),
  69. timeout=10,
  70. )
  71. if not url:
  72. ip = (
  73. "127.0.0.1"
  74. if "DOCKER" not in os.environ
  75. else subprocess.run(
  76. ["hostname", "-i"],
  77. stdout=subprocess.PIPE,
  78. check=True,
  79. )
  80. .stdout.decode("utf-8")
  81. .strip()
  82. )
  83. url = f"http://{ip}:{self.port}"
  84. self.url = url
  85. return url
  86. async def start(self, port: int, proxy_pass: bool = False):
  87. self.runner = web.AppRunner(self.app)
  88. await self.runner.setup()
  89. self.port = os.environ.get("PORT", port)
  90. site = web.TCPSite(self.runner, None, self.port)
  91. await site.start()
  92. await self.get_url(proxy_pass)
  93. self.running.set()
  94. async def stop(self):
  95. await self.runner.shutdown()
  96. await self.runner.cleanup()
  97. self.running.clear()
  98. self.ready.clear()
  99. async def add_loader(
  100. self,
  101. client: CustomTelegramClient,
  102. loader: Modules,
  103. db: Database,
  104. ):
  105. self.client_data[client.tg_id] = (loader, client, db)
  106. @staticmethod
  107. async def favicon(_):
  108. return web.Response(
  109. status=301,
  110. headers={"Location": "https://i.imgur.com/IRAiWBo.jpeg"},
  111. )