- 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 removal
pull/1/head
hikariatama 2022-05-14 17:46:52 +00:00
parent 5aad62610c
commit a6d01c6cdb
10 changed files with 131 additions and 66 deletions

View File

@ -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
View File

@ -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

View File

@ -0,0 +1,9 @@
version: "3"
services:
worker:
build: .
volumes:
- worker:/data
volumes:
worker:

View File

@ -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,9 +390,7 @@ 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:
async def save_client_session(self, client: TelegramClient):
session = SQLiteSession(
os.path.join(
self.arguments.data_root or DATA_DIR,
@ -409,12 +411,15 @@ class Hikka:
client.hikka_db = database.Database(client)
await client.hikka_db.init()
self.clients = list(set(self.clients + self.web.clients))
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']}",

View File

@ -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)),

View File

@ -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

View File

@ -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"""
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 = bool(os.popen('echo $PREFIX | grep -o "com.termux"').read())
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^)/※",
"٩(*❛⊰❛)~❤",
"( ͡~ ͜ʖ ͡°)",
"✧♡(◕‿◕✿)",
"โ๏௰๏ใ ื",
"∩。• ᵕ •。∩ ♡",
"(♡´౪`♡)",
"(◍>◡<◍)⋈。✧♡",
"♥(ˆ⌣ˆԅ)",
"╰(✿´⌣`✿)╯♡",
"ʕ•ᴥ•ʔ",
"ᶘ ◕ᴥ◕ᶅ",
"▼・ᴥ・▼",
"【≽ܫ≼】",
"ฅ^•ﻌ•^ฅ",
"(΄◞ิ౪◟ิ‵)",
"٩(^ᴗ^)۶",
"ᕴーᴥーᕵ",
"ʕ→ᴥ←ʔ",
"ʕᵕᴥᵕʔ",
"ʕᵒᴥᵒʔ",
"ᵔᴥᵔ",
"(✿╹◡╹)",
"(๑→ܫ←)",
"ʕ·ᴥ· ʔ",
"(ノ≧ڡ≦)",
"(≖ᴗ≖✿)",
"(〜^∇^ )〜",
"( ノ・ェ・ )ノ",
"~( ˘▾˘~)",
"(〜^∇^)〜",
"ヽ(^ᴗ^ヽ)",
"(´・ω・`)",
"₍ᐢ•ﻌ•ᐢ₎*・゚。",
"(。・・)_且",
"(=`ω´=)",
"(*•‿•*)",
"(*゚∀゚*)",
"(☉⋆‿⋆☉)",
"ɷ◡ɷ",
"ʘ‿ʘ",
"(。-ω-)ノ",
"( ・ω・)ノ",
"(=゚ω゚)ノ",
"(・ε・`*) …",
"ʕっ•ᴥ•ʔっ",
"(*˘︶˘*)",
]
)
)

View File

@ -1,2 +1,2 @@
"""Represents current userbot version"""
__version__ = (1, 1, 21)
__version__ = (1, 1, 22)

View File

@ -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()

View File

@ -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+