mirror of https://github.com/coddrago/Heroku
1.1.22
- Fix bugs related to web, more specifically: Session save timing, adding more than 1 account and proper restart - Rework Dockerfiles so they work properly - Add `uvloop` so the asyncio runs faster - Add `Docker` badge to info - Improve Okteto performance by adjusting settings in `okteto-stack.yml` - New ascii_faces in utils - Typehints update - Fix Okteto pinger messages removalpull/1/head
parent
5aad62610c
commit
a6d01c6cdb
|
@ -25,7 +25,7 @@ RUN apt update && apt install \
|
|||
libcairo2 git -y --no-install-recommends
|
||||
|
||||
# Clean the cache
|
||||
RUN rm -rf /var/lib/apt/lists /var/cache/apt/archives /tmp
|
||||
RUN rm -rf /var/lib/apt/lists /var/cache/apt/archives /tmp/*
|
||||
|
||||
# Expose IP address
|
||||
EXPOSE 8080
|
||||
|
|
2
Okteto
2
Okteto
|
@ -33,7 +33,7 @@ RUN apt update && apt install \
|
|||
libavdevice-dev -y --no-install-recommends
|
||||
|
||||
# Clean the cache
|
||||
RUN rm -rf /var/lib/apt/lists /var/cache/apt/archives /tmp
|
||||
RUN rm -rf /var/lib/apt/lists /var/cache/apt/archives /tmp/*
|
||||
|
||||
# Expose IP address
|
||||
EXPOSE 8080
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
version: "3"
|
||||
services:
|
||||
worker:
|
||||
build: .
|
||||
volumes:
|
||||
- worker:/data
|
||||
|
||||
volumes:
|
||||
worker:
|
|
@ -41,6 +41,8 @@ import sqlite3
|
|||
import sys
|
||||
from math import ceil
|
||||
from typing import Union
|
||||
import uvloop
|
||||
import subprocess
|
||||
|
||||
from telethon import TelegramClient, events
|
||||
from telethon.errors.rpcerrorlist import (
|
||||
|
@ -78,6 +80,8 @@ DATA_DIR = (
|
|||
|
||||
CONFIG_PATH = os.path.join(DATA_DIR, "config.json")
|
||||
|
||||
uvloop.install()
|
||||
|
||||
|
||||
def run_config(
|
||||
db: database.Database,
|
||||
|
@ -386,35 +390,36 @@ class Hikka:
|
|||
importlib.invalidate_caches()
|
||||
self._get_api_token()
|
||||
|
||||
async def fetch_clients_from_web(self):
|
||||
"""Imports clients from web module"""
|
||||
for client in self.web.clients:
|
||||
session = SQLiteSession(
|
||||
os.path.join(
|
||||
self.arguments.data_root or DATA_DIR,
|
||||
f"hikka-+{'x' * (len(client.phone) - 5)}{client.phone[-4:]}-{(await client.get_me()).id}",
|
||||
)
|
||||
async def save_client_session(self, client: TelegramClient):
|
||||
session = SQLiteSession(
|
||||
os.path.join(
|
||||
self.arguments.data_root or DATA_DIR,
|
||||
f"hikka-+{'x' * (len(client.phone) - 5)}{client.phone[-4:]}-{(await client.get_me()).id}",
|
||||
)
|
||||
)
|
||||
|
||||
session.set_dc(
|
||||
client.session.dc_id,
|
||||
client.session.server_address,
|
||||
client.session.port,
|
||||
)
|
||||
session.auth_key = client.session.auth_key
|
||||
session.save()
|
||||
client.session = session
|
||||
# Set db attribute to this client in order to save
|
||||
# custom bot nickname from web
|
||||
client.hikka_db = database.Database(client)
|
||||
await client.hikka_db.init()
|
||||
|
||||
self.clients = list(set(self.clients + self.web.clients))
|
||||
session.set_dc(
|
||||
client.session.dc_id,
|
||||
client.session.server_address,
|
||||
client.session.port,
|
||||
)
|
||||
session.auth_key = client.session.auth_key
|
||||
session.save()
|
||||
client.session = session
|
||||
# Set db attribute to this client in order to save
|
||||
# custom bot nickname from web
|
||||
client.hikka_db = database.Database(client)
|
||||
await client.hikka_db.init()
|
||||
|
||||
def _web_banner(self):
|
||||
"""Shows web banner"""
|
||||
print("✅ Web mode ready for configuration")
|
||||
print(f"🌐 Please visit http://127.0.0.1:{self.web.port}")
|
||||
ip = (
|
||||
"127.0.0.1"
|
||||
if "DOCKER" not in os.environ
|
||||
else subprocess.run(["hostname", "-i"], stdout=subprocess.PIPE).stdout
|
||||
)
|
||||
print(f"🌐 Please visit http://{ip}:{self.web.port}")
|
||||
|
||||
async def wait_for_web_auth(self, token: str):
|
||||
"""Waits for web auth confirmation in Telegram"""
|
||||
|
@ -449,9 +454,12 @@ class Hikka:
|
|||
|
||||
return True
|
||||
|
||||
def _init_clients(self):
|
||||
"""Reads session from disk and inits them"""
|
||||
for phone_id, phone in self.phones.items():
|
||||
def _init_clients(self) -> bool:
|
||||
"""
|
||||
Reads session from disk and inits them
|
||||
:returns: `True` if at least one client started successfully
|
||||
"""
|
||||
for phone_id, phone in self.phones.copy().items():
|
||||
session = os.path.join(
|
||||
self.arguments.data_root or DATA_DIR,
|
||||
f'hikka{f"-{phone_id}" if phone_id else ""}',
|
||||
|
@ -473,7 +481,7 @@ class Hikka:
|
|||
|
||||
install_entity_caching(client)
|
||||
|
||||
self.clients.append(client)
|
||||
self.clients += [client]
|
||||
except sqlite3.OperationalError:
|
||||
print(
|
||||
"Check that this is the only instance running. "
|
||||
|
@ -482,20 +490,22 @@ class Hikka:
|
|||
continue
|
||||
except (TypeError, AuthKeyDuplicatedError):
|
||||
os.remove(os.path.join(DATA_DIR, f"{session}.session"))
|
||||
self.main()
|
||||
del self.phones[phone_id]
|
||||
except (ValueError, ApiIdInvalidError):
|
||||
# Bad API hash/ID
|
||||
run_config({}, self.arguments.data_root)
|
||||
return
|
||||
return False
|
||||
except PhoneNumberInvalidError:
|
||||
print(
|
||||
"Phone number is incorrect. Use international format (+XX...) "
|
||||
"and don't put spaces in it."
|
||||
)
|
||||
continue
|
||||
del self.phones[phone_id]
|
||||
except InteractiveAuthRequired:
|
||||
print(f"Session {session} was terminated and re-auth is required")
|
||||
continue
|
||||
del self.phones[phone_id]
|
||||
|
||||
return bool(self.phones)
|
||||
|
||||
def _init_loop(self):
|
||||
"""Initializes main event loop and starts handler for each client"""
|
||||
|
@ -654,11 +664,13 @@ class Hikka:
|
|||
save_config_key("port", self.arguments.port)
|
||||
self._get_token()
|
||||
|
||||
if not self.clients and not self.phones and not self._initial_setup():
|
||||
if (
|
||||
not self.clients # Search for already inited clients
|
||||
and not self.phones # Search for already added phones / sessions
|
||||
or not self._init_clients() # Attempt to read sessions from env
|
||||
) and not self._initial_setup(): # Otherwise attempt to run setup
|
||||
return
|
||||
|
||||
self._init_clients()
|
||||
|
||||
self.loop.set_exception_handler(
|
||||
lambda _, x: logging.error(
|
||||
f"Exception on event loop! {x['message']}",
|
||||
|
|
|
@ -477,7 +477,7 @@ class HikkaSecurityMod(loader.Module):
|
|||
list(set(self._db.get(main.__name__, "nonickusers", []) + [user.id])),
|
||||
)
|
||||
|
||||
call.edit(
|
||||
await call.edit(
|
||||
self.strings("user_nn").format(
|
||||
user.id,
|
||||
utils.escape_html(get_display_name(user)),
|
||||
|
|
|
@ -15,7 +15,10 @@ import time
|
|||
|
||||
from telethon.errors.rpcerrorlist import YouBlockedUserError
|
||||
from telethon.tl.functions.contacts import UnblockRequest
|
||||
from telethon.tl.functions.messages import GetScheduledHistoryRequest
|
||||
from telethon.tl.functions.messages import (
|
||||
GetScheduledHistoryRequest,
|
||||
DeleteScheduledMessagesRequest,
|
||||
)
|
||||
from telethon.tl.types import Message
|
||||
|
||||
from .. import loader, main, utils
|
||||
|
@ -54,8 +57,12 @@ class OktetoMod(loader.Module):
|
|||
if messages:
|
||||
logger.info("Deleting previously scheduled Okteto pinger messages")
|
||||
|
||||
for message in messages:
|
||||
await message.delete()
|
||||
await client(
|
||||
DeleteScheduledMessagesRequest(
|
||||
self._bot,
|
||||
[message.id for message in messages],
|
||||
)
|
||||
)
|
||||
|
||||
raise loader.SelfUnload
|
||||
|
||||
|
|
|
@ -45,7 +45,6 @@ import git
|
|||
import grapheme
|
||||
import requests
|
||||
import telethon
|
||||
from aiogram.types import CallbackQuery
|
||||
from telethon.hints import Entity
|
||||
from telethon.tl.custom.message import Message
|
||||
from telethon.tl.functions.account import UpdateNotifySettingsRequest
|
||||
|
@ -83,7 +82,7 @@ from telethon.tl.types import (
|
|||
User,
|
||||
)
|
||||
|
||||
from .inline.types import InlineCall
|
||||
from .inline.types import InlineCall, InlineMessage
|
||||
|
||||
FormattingEntity = Union[
|
||||
MessageEntityUnknown,
|
||||
|
@ -279,16 +278,16 @@ def relocate_entities(
|
|||
|
||||
|
||||
async def answer(
|
||||
message: Union[Message, CallbackQuery, InlineCall],
|
||||
message: Union[Message, InlineCall, InlineMessage],
|
||||
response: str,
|
||||
**kwargs,
|
||||
) -> Union[CallbackQuery, Message]:
|
||||
) -> Union[InlineCall, InlineMessage, Message]:
|
||||
"""Use this to give the response to a command"""
|
||||
# Compatibility with FTG\GeekTG
|
||||
if isinstance(message, list) and message:
|
||||
message = message[0]
|
||||
|
||||
if isinstance(message, (CallbackQuery, InlineCall)):
|
||||
if isinstance(message, (InlineMessage, InlineCall)):
|
||||
await message.edit(response)
|
||||
return message
|
||||
|
||||
|
@ -578,13 +577,16 @@ def chunks(_list: Union[list, tuple, set], n: int, /) -> list:
|
|||
|
||||
def get_named_platform() -> str:
|
||||
"""Returns formatted platform name"""
|
||||
if os.path.isfile("/proc/device-tree/model"):
|
||||
with open("/proc/device-tree/model") as f:
|
||||
model = f.read()
|
||||
return f"🍇 {model}" if model.startswith("Raspberry") else f"❓ {model}"
|
||||
|
||||
is_termux = bool(os.popen('echo $PREFIX | grep -o "com.termux"').read())
|
||||
try:
|
||||
if os.path.isfile("/proc/device-tree/model"):
|
||||
with open("/proc/device-tree/model") as f:
|
||||
model = f.read()
|
||||
return f"🍇 {model}" if model.startswith("Raspberry") else f"❓ {model}"
|
||||
except Exception:
|
||||
# In case of weird fs, aka Termux
|
||||
pass
|
||||
|
||||
is_termux = "com.termux" in os.environ.get("PREFIX", "")
|
||||
is_okteto = "OKTETO" in os.environ
|
||||
is_docker = "DOCKER" in os.environ
|
||||
is_lavhost = "LAVHOST" in os.environ
|
||||
|
@ -620,18 +622,13 @@ def ascii_face() -> str:
|
|||
random.choice(
|
||||
[
|
||||
"ヽ(๑◠ܫ◠๑)ノ",
|
||||
"☜(⌒▽⌒)☞",
|
||||
"/|\\ ^._.^ /|\\",
|
||||
"(◕ᴥ◕ʋ)",
|
||||
"ᕙ(`▽´)ᕗ",
|
||||
"(☞゚∀゚)☞",
|
||||
"(✿◠‿◠)",
|
||||
"(▰˘◡˘▰)",
|
||||
"(˵ ͡° ͜ʖ ͡°˵)",
|
||||
"ʕっ•ᴥ•ʔっ",
|
||||
"( ͡° ᴥ ͡°)",
|
||||
"ʕ♥ᴥ♥ʔ",
|
||||
"\\m/,(> . <)_\\m/",
|
||||
"(๑•́ ヮ •̀๑)",
|
||||
"٩(^‿^)۶",
|
||||
"(っˆڡˆς)",
|
||||
|
@ -639,22 +636,49 @@ def ascii_face() -> str:
|
|||
"⊙ω⊙",
|
||||
"٩(^ᴗ^)۶",
|
||||
"(´・ω・)っ由",
|
||||
"※\\(^o^)/※",
|
||||
"٩(*❛⊰❛)~❤",
|
||||
"( ͡~ ͜ʖ ͡°)",
|
||||
"✧♡(◕‿◕✿)",
|
||||
"โ๏௰๏ใ ื",
|
||||
"∩。• ᵕ •。∩ ♡",
|
||||
"(♡´౪`♡)",
|
||||
"(◍>◡<◍)⋈。✧♡",
|
||||
"♥(ˆ⌣ˆԅ)",
|
||||
"╰(✿´⌣`✿)╯♡",
|
||||
"ʕ•ᴥ•ʔ",
|
||||
"ᶘ ◕ᴥ◕ᶅ",
|
||||
"▼・ᴥ・▼",
|
||||
"【≽ܫ≼】",
|
||||
"ฅ^•ﻌ•^ฅ",
|
||||
"(΄◞ิ౪◟ิ‵)",
|
||||
"٩(^ᴗ^)۶",
|
||||
"ᕴーᴥーᕵ",
|
||||
"ʕ→ᴥ←ʔ",
|
||||
"ʕᵕᴥᵕʔ",
|
||||
"ʕᵒᴥᵒʔ",
|
||||
"ᵔᴥᵔ",
|
||||
"(✿╹◡╹)",
|
||||
"(๑→ܫ←)",
|
||||
"ʕ·ᴥ· ʔ",
|
||||
"(ノ≧ڡ≦)",
|
||||
"(≖ᴗ≖✿)",
|
||||
"(〜^∇^ )〜",
|
||||
"( ノ・ェ・ )ノ",
|
||||
"~( ˘▾˘~)",
|
||||
"(〜^∇^)〜",
|
||||
"ヽ(^ᴗ^ヽ)",
|
||||
"(´・ω・`)",
|
||||
"₍ᐢ•ﻌ•ᐢ₎*・゚。",
|
||||
"(。・・)_且",
|
||||
"(=`ω´=)",
|
||||
"(*•‿•*)",
|
||||
"(*゚∀゚*)",
|
||||
"(☉⋆‿⋆☉)",
|
||||
"ɷ◡ɷ",
|
||||
"ʘ‿ʘ",
|
||||
"(。-ω-)ノ",
|
||||
"( ・ω・)ノ",
|
||||
"(=゚ω゚)ノ",
|
||||
"(・ε・`*) …",
|
||||
"ʕっ•ᴥ•ʔっ",
|
||||
"(*˘︶˘*)",
|
||||
]
|
||||
)
|
||||
)
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
"""Represents current userbot version"""
|
||||
__version__ = (1, 1, 21)
|
||||
__version__ = (1, 1, 22)
|
||||
|
|
|
@ -67,7 +67,7 @@ def restart(*argv):
|
|||
|
||||
class Web:
|
||||
sign_in_clients = {}
|
||||
clients = []
|
||||
_pending_clients = []
|
||||
_sessions = []
|
||||
_ratelimit = {}
|
||||
|
||||
|
@ -151,7 +151,7 @@ class Web:
|
|||
return web.Response(status=401)
|
||||
|
||||
text = await request.text()
|
||||
client = self.clients[0]
|
||||
client = self._pending_clients[0]
|
||||
db = database.Database(client)
|
||||
await db.init()
|
||||
|
||||
|
@ -287,18 +287,30 @@ class Web:
|
|||
del self.sign_in_clients[phone]
|
||||
|
||||
client.phone = f"+{user.phone}"
|
||||
self.clients.append(client)
|
||||
|
||||
# At this step we don't want `main.hikka` to "know" about our client
|
||||
# so it doesn't create bot immediately. That's why we only save its session
|
||||
# in case user closes web early. It will be handled on restart
|
||||
# If user finishes login further, client will be passed to main
|
||||
await main.hikka.save_client_session(client)
|
||||
|
||||
# But now it's pending
|
||||
self._pending_clients += [client]
|
||||
|
||||
return web.Response()
|
||||
|
||||
async def finish_login(self, request):
|
||||
if not self._check_session(request):
|
||||
return web.Response(status=401)
|
||||
|
||||
if not self.clients:
|
||||
if not self._pending_clients:
|
||||
return web.Response(status=400)
|
||||
|
||||
first_session = not bool(main.hikka.clients)
|
||||
await main.hikka.fetch_clients_from_web()
|
||||
|
||||
# Client is ready to pass in to dispatcher
|
||||
main.hikka.clients = list(set(main.hikka.clients + self._pending_clients))
|
||||
self._pending_clients = []
|
||||
|
||||
self.clients_set.set()
|
||||
|
||||
|
|
|
@ -9,5 +9,6 @@ requests==2.27.1
|
|||
aiogram==2.19
|
||||
websockets==10.2
|
||||
grapheme==0.6.0
|
||||
uvloop==0.16.0
|
||||
|
||||
# Python 3.8+
|
||||
|
|
Loading…
Reference in New Issue