Skip to content

Commit ce1f24f

Browse files
authored
Merge pull request beveiled#110 from hikariatama/v1.6.3
V1.6.3
2 parents 06f5290 + 1fa4338 commit ce1f24f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+2230
-981
lines changed

.flake8

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
[flake8]
2-
ignore = E501,W503,E203,ASN001,E701,E231,E225,W504
2+
ignore = E501,E203,E701,ASN001,E231,E503,W503,E225,W504
33
exclude = .git,__pycache__,loaded_modules

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,23 @@
11
# Hikka Changelog
22

3+
## 🌑 Hikka 1.6.3
4+
5+
- Add argument `item_type` to `db.pointer` to provide interface for NamedTuple objects
6+
- Add correct exception propagation to inline units
7+
- Add `-fs` arg to `.lm` command
8+
- Add IDM `flush_loader_cache`
9+
- Add ability to cancel QR login using keyboard interrupt
10+
- Add custom security groups
11+
- Add automatic NoNick for tsec methods
12+
- Add external debugging feature (off by default)
13+
- Fix form invoke error message
14+
- Fix `Backuper`
15+
- Fix backward compatiblity with security groups `SUDO` (0x2) and `SUPPORT` (0x4)
16+
- Fix quickstart language buttons translations
17+
- Fix language buttons disabling on restart
18+
- Migrate inline heta search to userbot instead of centralized service
19+
- Cosmetical changes
20+
321
## 🌑 Hikka 1.6.2
422

523
- Fix security issue with edited channel messages

hikka/__main__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,12 @@ def deps():
6060
try:
6161
import hikkatl # noqa: F811
6262

63-
if tuple(map(int, hikkatl.__version__.split("."))) < (2, 0, 3):
63+
if tuple(map(int, hikkatl.__version__.split("."))) < (2, 0, 4):
6464
raise ImportError
6565

6666
import hikkapyro
6767

68-
if tuple(map(int, hikkapyro.__version__.split("."))) < (2, 0, 102):
68+
if tuple(map(int, hikkapyro.__version__.split("."))) < (2, 0, 103):
6969
raise ImportError
7070
except ImportError:
7171
print("🔄 Installing dependencies...")

hikka/_local_storage.py

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
import requests
1616

1717
from . import utils
18+
from .tl_cache import CustomTelegramClient
19+
from .version import __version__
1820

1921
logger = logging.getLogger(__name__)
2022

@@ -45,7 +47,12 @@ def _get_path(self, repo: str, module_name: str) -> str:
4547
)
4648

4749
def save(self, repo: str, module_name: str, module_code: str):
48-
"""Saves module to disk."""
50+
"""
51+
Saves module to disk.
52+
:param repo: Repository name.
53+
:param module_name: Module name.
54+
:param module_code: Module source code.
55+
"""
4956
size = len(module_code)
5057
if size > MAX_FILESIZE:
5158
logger.warning(
@@ -70,7 +77,12 @@ def save(self, repo: str, module_name: str, module_code: str):
7077
logger.debug("Saved module %s from %s to local cache.", module_name, repo)
7178

7279
def fetch(self, repo: str, module_name: str) -> typing.Optional[str]:
73-
"""Fetches module from disk."""
80+
"""
81+
Fetches module from disk.
82+
:param repo: Repository name.
83+
:param module_name: Module name.
84+
:return: Module source code or None.
85+
"""
7486
path = self._get_path(repo, module_name)
7587
if os.path.isfile(path):
7688
with open(path, "r") as f:
@@ -80,8 +92,9 @@ def fetch(self, repo: str, module_name: str) -> typing.Optional[str]:
8092

8193

8294
class RemoteStorage:
83-
def __init__(self):
95+
def __init__(self, client: CustomTelegramClient):
8496
self._local_storage = LocalStorage()
97+
self._client = client
8598

8699
async def preload(self, urls: typing.List[str]):
87100
"""Preloads modules from remote storage."""
@@ -122,7 +135,11 @@ async def preload_main_repo(self):
122135

123136
@staticmethod
124137
def _parse_url(url: str) -> typing.Tuple[str, str, str]:
125-
"""Parses a URL into a repository and module name."""
138+
"""
139+
Parses a URL into a repository and module name.
140+
:param url: URL to parse.
141+
:return: Tuple of (url, repo, module_name).
142+
"""
126143
domain_name = url.split("/")[2]
127144

128145
if domain_name == "raw.githubusercontent.com":
@@ -142,14 +159,22 @@ def _parse_url(url: str) -> typing.Tuple[str, str, str]:
142159
async def fetch(self, url: str, auth: typing.Optional[str] = None) -> str:
143160
"""
144161
Fetches the module from the remote storage.
145-
:param ref: The module reference. Can be url, or a reference to official repo module.
162+
:param url: URL to the module.
163+
:param auth: Optional authentication string in the format "username:password".
164+
:return: Module source code.
146165
"""
147166
url, repo, module_name = self._parse_url(url)
148167
try:
149168
r = await utils.run_sync(
150169
requests.get,
151170
url,
152171
auth=(tuple(auth.split(":", 1)) if auth else None),
172+
headers={
173+
"User-Agent": "Hikka Userbot",
174+
"X-Hikka-Version": ".".join(map(str, __version__)),
175+
"X-Hikka-Commit-SHA": utils.get_git_hash(),
176+
"X-Hikka-User": str(self._client.tg_id),
177+
},
153178
)
154179
r.raise_for_status()
155180
except Exception:

hikka/compat/dragon.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ def format_small_module_help(self, module_name: str):
274274
" list:</b>\n"
275275
)
276276

277-
for command, desc in commands.items():
277+
for command in commands:
278278
cmd = command.split(maxsplit=1)
279279
args = " <code>" + cmd[1] + "</code>" if len(cmd) > 1 else ""
280280
help_text += f"<code>{self.misc.prefix}{cmd[0]}</code>{args}\n"

hikka/compat/pyroproxy.py

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from hikkapyro import errors as pyro_errors
1818
from hikkapyro import raw
1919

20-
from .. import translations, utils
20+
from .. import utils
2121
from ..tl_cache import CustomTelegramClient
2222
from ..version import __version__
2323

@@ -56,19 +56,13 @@ class PyroProxyClient(PyroClient):
5656
def __init__(self, tl_client: CustomTelegramClient):
5757
self.tl_client = tl_client
5858
super().__init__(
59-
**{
60-
"name": "proxied_pyrogram_client",
61-
"api_id": tl_client.api_id,
62-
"api_hash": tl_client.api_hash,
63-
"app_version": (
64-
f"Hikka v{__version__[0]}.{__version__[1]}.{__version__[2]}"
65-
),
66-
"lang_code": tl_client.loader.db.get(
67-
translations.__name__, "lang", "en"
68-
).split()[0],
69-
"in_memory": True,
70-
"phone_number": tl_client.hikka_me.phone,
71-
}
59+
name="proxied_pyrogram_client",
60+
api_id=tl_client.api_id,
61+
api_hash=tl_client.api_hash,
62+
app_version=".".join(map(str, __version__)) + " x64",
63+
lang_code="en",
64+
in_memory=True,
65+
phone_number=tl_client.hikka_me.phone,
7266
)
7367

7468
# We need to set this to True so pyro thinks he's connected

hikka/database.py

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,27 @@
2424
from hikkatl.tl.types import Message, User
2525

2626
from . import main, utils
27-
from .pointers import PointerDict, PointerList
27+
from .pointers import (
28+
BaseSerializingMiddlewareDict,
29+
BaseSerializingMiddlewareList,
30+
NamedTupleMiddlewareDict,
31+
NamedTupleMiddlewareList,
32+
PointerDict,
33+
PointerList,
34+
)
2835
from .tl_cache import CustomTelegramClient
2936
from .types import JSONSerializable
3037

38+
__all__ = [
39+
"Database",
40+
"PointerList",
41+
"PointerDict",
42+
"NamedTupleMiddlewareDict",
43+
"NamedTupleMiddlewareList",
44+
"BaseSerializingMiddlewareDict",
45+
"BaseSerializingMiddlewareList",
46+
]
47+
3148
logger = logging.getLogger(__name__)
3249

3350

@@ -287,6 +304,7 @@ def pointer(
287304
owner: str,
288305
key: str,
289306
default: typing.Optional[JSONSerializable] = None,
307+
item_type: typing.Optional[typing.Any] = None,
290308
) -> typing.Union[JSONSerializable, PointerList, PointerDict]:
291309
"""Get a pointer to database key"""
292310
value = self.get(owner, key, default)
@@ -306,4 +324,30 @@ def pointer(
306324
f"Pointer for type {type(value).__name__} is not implemented"
307325
)
308326

327+
if item_type is not None:
328+
if isinstance(value, list):
329+
for item in self.get(owner, key, default):
330+
if not isinstance(item, dict):
331+
raise ValueError(
332+
"Item type can only be specified for dedicated keys and"
333+
" can't be mixed with other ones"
334+
)
335+
336+
return NamedTupleMiddlewareList(
337+
pointer_constructor(self, owner, key, default),
338+
item_type,
339+
)
340+
if isinstance(value, dict):
341+
for item in self.get(owner, key, default).values():
342+
if not isinstance(item, dict):
343+
raise ValueError(
344+
"Item type can only be specified for dedicated keys and"
345+
" can't be mixed with other ones"
346+
)
347+
348+
return NamedTupleMiddlewareDict(
349+
pointer_constructor(self, owner, key, default),
350+
item_type,
351+
)
352+
309353
return pointer_constructor(self, owner, key, default)

hikka/dispatcher.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,7 @@ async def _handle_command(
359359
and not self._db.get(main.__name__, "no_nickname", False)
360360
and command not in self._db.get(main.__name__, "nonickcmds", [])
361361
and initiator not in self._db.get(main.__name__, "nonickusers", [])
362+
and not self.security.check_tsec(initiator, command)
362363
and utils.get_chat_id(event)
363364
not in self._db.get(main.__name__, "nonickchats", [])
364365
):
@@ -369,7 +370,11 @@ async def _handle_command(
369370
if (
370371
not func
371372
or not await self._handle_ratelimit(message, func)
372-
or not await self.security.check(message, func)
373+
or not await self.security.check(
374+
message,
375+
func,
376+
usernames=self._cached_usernames,
377+
)
373378
):
374379
return False
375380

hikka/inline/core.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
# 🔑 https://www.gnu.org/licenses/agpl-3.0.html
88

99
import asyncio
10+
import contextlib
1011
import logging
1112
import time
1213
import typing
@@ -16,8 +17,10 @@
1617
from aiogram.utils.exceptions import TerminatedByOtherGetUpdates, Unauthorized
1718
from hikkatl.errors.rpcerrorlist import InputUserDeactivatedError, YouBlockedUserError
1819
from hikkatl.tl.functions.contacts import UnblockRequest
20+
from hikkatl.tl.types import Message
1921
from hikkatl.utils import get_display_name
2022

23+
from .. import utils
2124
from ..database import Database
2225
from ..tl_cache import CustomTelegramClient
2326
from ..translations import Translator
@@ -69,6 +72,7 @@ def __init__(
6972
self._custom_map: typing.Dict[str, callable] = {}
7073
self.fsm: typing.Dict[str, str] = {}
7174
self._web_auth_tokens: typing.List[str] = []
75+
self._error_events: typing.Dict[str, asyncio.Event] = {}
7276

7377
self._markup_ttl = 60 * 60 * 24
7478
self.init_complete = False
@@ -216,3 +220,47 @@ def pop_web_auth_token(self, token: str) -> bool:
216220

217221
self._web_auth_tokens.remove(token)
218222
return True
223+
224+
async def _invoke_unit(self, unit_id: str, message: Message) -> Message:
225+
event = asyncio.Event()
226+
self._error_events[unit_id] = event
227+
228+
q: "InlineResults" = None # type: ignore # noqa: F821
229+
exception: Exception = None
230+
231+
async def result_getter():
232+
nonlocal unit_id, q
233+
with contextlib.suppress(Exception):
234+
q = await self._client.inline_query(self.bot_username, unit_id)
235+
236+
async def event_poller():
237+
nonlocal exception
238+
await asyncio.wait_for(event.wait(), timeout=10)
239+
if self._error_events.get(unit_id):
240+
exception = self._error_events[unit_id]
241+
242+
result_getter_task = asyncio.ensure_future(result_getter())
243+
event_poller_task = asyncio.ensure_future(event_poller())
244+
245+
_, pending = await asyncio.wait(
246+
[result_getter_task, event_poller_task],
247+
return_when=asyncio.FIRST_COMPLETED,
248+
)
249+
250+
for task in pending:
251+
task.cancel()
252+
253+
self._error_events.pop(unit_id, None)
254+
255+
if exception:
256+
raise exception # skipcq: PYL-E0702
257+
258+
if not q:
259+
raise Exception("No query results")
260+
261+
return await q[0].click(
262+
utils.get_chat_id(message) if isinstance(message, Message) else message,
263+
reply_to=(
264+
message.reply_to_msg_id if isinstance(message, Message) else None
265+
),
266+
)

hikka/inline/events.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ async def _message_handler(self, message: AiogramMessage):
3838
if (
3939
not hasattr(mod, "aiogram_watcher")
4040
or message.text == "/start"
41-
and mod.__class__.__name__ != "InlineStuffMod"
41+
and mod.__class__.__name__ != "InlineStuff"
4242
):
4343
continue
4444

0 commit comments

Comments
 (0)