Skip to content

Commit

Permalink
add caching and improve (fixes some bugs) (#16)
Browse files Browse the repository at this point in the history
* Enhance caching logic for chat member retrieval

- Implemented TTLCache for storing chat members with a 30-minute expiration.
- Utilized caching to reduce API calls and improve performance.

* improved code with reduced duplication

* bump version
  • Loading branch information
AshokShau authored Oct 13, 2024
1 parent 61b33db commit 6fbe1a7
Show file tree
Hide file tree
Showing 9 changed files with 322 additions and 202 deletions.
10 changes: 9 additions & 1 deletion README.md → .github/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ pip install -U Abg # For Pyrogram or Pyrogram Forks
```bash
pip install -U Abg[hydrogram] # For Hydrogram
```
```bash
pip install -U Abg[pyrofork] # for pyrofork
```


### Getting Started
```python
Expand Down Expand Up @@ -60,7 +64,11 @@ from hydrogram import Client
app = Client("my_account")

@app.on_cmd("del", group_only=True)
@app.adminsOnly(permissions="can_delete_messages", is_both=True)
@adminsOnly(
self=app,
permissions="can_delete_messages",
is_both=True,
) # also you can use like this @app.adminsOnly(permissions="can_delete_messages", is_both=True)
async def del_msg(self: Client, m: Message):
if m.reply_to_message:
await m.delete()
Expand Down
2 changes: 1 addition & 1 deletion Abg/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@
log = logging.getLogger(__name__)
log.info(f"Version: {__version__}\nCopyright: {__copyright__}")

from .patch import * # noqa
from .patch import * # types :ignore
2 changes: 1 addition & 1 deletion Abg/__version__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "2.4"
__version__ = "2.5"
337 changes: 156 additions & 181 deletions Abg/patch/decorators/adminsOnly.py

Large diffs are not rendered by default.

58 changes: 58 additions & 0 deletions Abg/patch/decorators/cache.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from logging import getLogger
from time import perf_counter
from typing import Any

from cachetools import TTLCache
from cachetools.keys import hashkey

LOGGER = getLogger(__name__)

try:
import pyrogram
except ImportError:
import hydrogram as pyrogram

# Admins stay cached for 30 minutes
member_cache = TTLCache(maxsize=512, ttl=(60 * 30), timer=perf_counter)


async def get_member_with_cache(
chat: pyrogram.types.Chat,
user_id: int,
force_reload: bool = False,
) -> pyrogram.types.ChatMember | None | Any:
"""
Get a user from the cache, or fetch and cache them if they're not already cached.
Args:
chat (pyrogram.types.Chat): The chat to get the user from.
user_id (int): The user ID to get.
force_reload (bool): Whether to bypass the cache and reload the member.
Returns:
pyrogram.types.ChatMember | None | Any: The user, or None if they're not a participant or if an error occurred.
"""
cache_key = hashkey(chat.id, user_id)

# Check if the member is in the cache and not forcing a reload
if not force_reload and cache_key in member_cache:
return member_cache[cache_key]

try:
member = await chat.get_member(user_id)
except pyrogram.errors.UserNotParticipant:
LOGGER.warning(f"User {user_id} is not a participant in chat {chat.id}.")
return None
except Exception as e:
LOGGER.warning(f"Error found in get_member_with_cache for chat {chat.id}, user {user_id}: {e}")
return None

# Store in cache and return
member_cache[cache_key] = member
return member


async def is_admin(member: pyrogram.types.ChatMember) -> bool:
"""Check if the user is an admin in the chat."""
return member and member.status in {pyrogram.enums.ChatMemberStatus.OWNER,
pyrogram.enums.ChatMemberStatus.ADMINISTRATOR}
7 changes: 4 additions & 3 deletions Abg/patch/decorators/on_cb.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ async def decorator(abg: pyrogram.Client, q: pyrogram.types.CallbackQuery):
me = await abg.get_chat_member(
q.message.chat.id, (await abg.get_me()).id
)
if me.status not in (
if me and me.status not in (
pyrogram.enums.ChatMemberStatus.OWNER,
pyrogram.enums.ChatMemberStatus.ADMINISTRATOR,
):
Expand All @@ -84,7 +84,7 @@ async def decorator(abg: pyrogram.Client, q: pyrogram.types.CallbackQuery):
except Exception as e:
LOGGER.error("Error while fetching user status: " + str(e))
return
if user.status not in (
if user and user.status not in (
pyrogram.enums.ChatMemberStatus.OWNER,
pyrogram.enums.ChatMemberStatus.ADMINISTRATOR,
):
Expand All @@ -107,7 +107,8 @@ async def decorator(abg: pyrogram.Client, q: pyrogram.types.CallbackQuery):
"I must be admin to execute this command.", show_alert=True
)
except Exception as e:
return LOGGER.error(f"Error while executing command: {e}")
LOGGER.error(f"Error while executing command: {e}")
return

self.add_handler(pyrogram.handlers.CallbackQueryHandler(decorator, filtercb))
return decorator
Expand Down
36 changes: 22 additions & 14 deletions Abg/patch/decorators/on_cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,17 @@
def command(
self,
cmd: typing.Union[str, list],
is_disabled: typing.Union[bool, bool] = False,
pm_only: typing.Union[bool, bool] = False,
group_only: typing.Union[bool, bool] = False,
self_admin: typing.Union[bool, bool] = False,
self_only: typing.Union[bool, bool] = False,
is_disabled: bool = False,
pm_only: bool = False,
group_only: bool = False,
self_admin: bool = False,
self_only: bool = False,
handler: typing.Optional[list] = None,
filtercmd: typing.Union[pyrogram.filters.Filter, pyrogram.filters.Filter] = None,
filtercmd: typing.Optional[pyrogram.filters.Filter] = None,
*args,
**kwargs,
):
) -> typing.Callable[[typing.Callable[[pyrogram.Client, pyrogram.types.Message], typing.Any]], typing.Callable[
[pyrogram.Client, pyrogram.types.Message], typing.Any]]:
"""
### `@Client.on_cmd` - A Decorator to Register Commands in a simple way and manage errors in that Function itself,
alternative for `@hydrogram.Client.on_message(hydrogram.filters.command('command'))`
Expand Down Expand Up @@ -67,6 +68,7 @@ async def start(abg: Client, message):
"""
if handler is None:
handler = HANDLER

if filtercmd:
if self_only:
filtercmd = (
Expand All @@ -78,7 +80,6 @@ async def start(abg: Client, message):
filtercmd = (
pyrogram.filters.command(cmd, prefixes=handler)
& filtercmd
& pyrogram.filters.me
)
else:
if self_only:
Expand All @@ -94,11 +95,15 @@ async def decorator(abg: pyrogram.Client, message: pyrogram.types.Message):
return await message.reply_text(
"This command is disabled by the Admins."
)
if group_only:
if message.chat.type != pyrogram.enums.ChatType.SUPERGROUP or message.chat.type != pyrogram.enums.ChatType.GROUP:
return await message.reply_text(
"This command can be used in supergroups only."
)

if group_only and message.chat.type not in (
pyrogram.enums.ChatType.GROUP,
pyrogram.enums.ChatType.SUPERGROUP,
):
return await message.reply_text(
"This command can be used in supergroups only."
)

if self_admin:
me = await abg.get_chat_member(message.chat.id, (await abg.get_me()).id)
if me.status not in (
Expand All @@ -111,6 +116,7 @@ async def decorator(abg: pyrogram.Client, message: pyrogram.types.Message):

if pm_only and message.chat.type != pyrogram.enums.ChatType.PRIVATE:
return await message.reply_text("This command can be used in PM only.")

try:
await func(abg, message, *args, **kwargs)
except pyrogram.StopPropagation:
Expand All @@ -121,7 +127,9 @@ async def decorator(abg: pyrogram.Client, message: pyrogram.types.Message):
LOGGER.warning("Sleeping for {fw.value}, Due to flood")
await asyncio.sleep(fw.value)
except (errors.Forbidden, errors.SlowmodeWait):
LOGGER.warning(f"Forbidden : {message.chat.title} [{message.chat.id}] doesn't have write permission.")
LOGGER.warning(
f"Forbidden : {message.chat.title} [{message.chat.id}] doesn't have write permission."
)
return # await message.chat.leave()
except Exception as e:
return LOGGER.error(f"Error while executing command: {e}")
Expand Down
61 changes: 61 additions & 0 deletions example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
from hydrogram import Client
from hydrogram.helpers import ikb
from hydrogram.types import CallbackQuery, Message

from Abg import * # type: ignore

app = Client(
name='Abg',
api_id=6,
api_hash='eb06d4abfb49dc3eeb1aeb98ae0f581e',
bot_token="TOKEN",
in_memory=True,
)


@app.on_cmd("start")
async def start(self: Client, ctx: Message):
"""
Sends a Hello World message with an inline button that triggers the hello callback.
"""
await ctx.reply_text(
"Hello World",
reply_markup=ikb([[("Hello", "hello")]])
)


@app.on_cb("hello")
async def hello(_: Client, q: CallbackQuery):
"""
Called when the user presses the "Hello" button in the start command.
"""
await q.answer("Hello From Abg", show_alert=True)


@app.on_cmd("del", group_only=True)
@app.adminsOnly(
permissions=["can_delete_messages", "can_restrict_members"],
is_both=True,
)
async def del_msg(self: Client, m: Message):
"""
Deletes a message from the chat.
If the message is a reply to another message, it deletes that message too.
"""
if m.reply_to_message:
# Delete the replied message
await self.delete_messages(
chat_id=m.chat.id,
message_ids=[m.reply_to_message.id],
)
# Delete the command message
await m.delete()
else:
# If the message is not a reply, reply with an error message
await m.reply_text(text="You need to reply to a message to delete it.", quote=True)


if __name__ == "__main__":
print("Running...")
app.run()
11 changes: 10 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
exec(f.read(), about)

DESCRIPTION = 'add-on for Pyrogram || add-on for Hydrogram || Telegram bot helpers || Easy botting'
with open("README.md", encoding="utf8") as readme:
with open(".github/README.md", encoding="utf8") as readme:
long_description = readme.read()

setup(
Expand All @@ -36,7 +36,16 @@
extras_require={
'hydrogram': [
'hydrogram',
'TgCrypto-pyrofork'
],
'pyrofork': [
'pyrofork'
'TgCrypto-pyrofork'
],
'dev': [
'setuptools'
'twine',
]
},
keywords="add-on bots telegram bot hydrogram pyrogram",
url="https://github.com/AshokShau/Abg",
Expand Down

0 comments on commit 6fbe1a7

Please sign in to comment.