telegram_libs 0.1.26__tar.gz → 0.1.28__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: telegram_libs
3
- Version: 0.1.26
3
+ Version: 0.1.28
4
4
  Summary: Common libraries for Telegram bots
5
5
  Author: Andrey Gritsaenko gricaenko.95a@gmail.com
6
6
  Requires-Python: >=3.11,<4.0
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "telegram_libs"
3
- version = "0.1.26"
3
+ version = "0.1.28"
4
4
  description = "Common libraries for Telegram bots"
5
5
  authors = ["Andrey Gritsaenko gricaenko.95a@gmail.com"]
6
6
  readme = "README.md"
@@ -1,6 +1,9 @@
1
+ from datetime import datetime
2
+ from telegram import Update
1
3
  from pymongo.mongo_client import MongoClient
2
4
  from pymongo.server_api import ServerApi
3
- from telegram_libs.constants import MONGO_URI, DEBUG
5
+ from telegram_libs.constants import MONGO_URI, DEBUG, SUBSCRIPTION_DB_NAME
6
+ from telegram import Update
4
7
 
5
8
 
6
9
  class MongoManager:
@@ -18,6 +21,11 @@ class MongoManager:
18
21
  self.users_collection = self.db["users_test"] if DEBUG else self.db["users"]
19
22
  self.payments_collection = self.db["order_test"] if DEBUG else self.db["order"]
20
23
  self.user_schema = {"user_id": None, **(kwargs.get("user_schema") or {})}
24
+ self.subscription_collection = (
25
+ self.client[SUBSCRIPTION_DB_NAME]["subscriptions"]
26
+ if not DEBUG
27
+ else self.client[SUBSCRIPTION_DB_NAME]["subscriptions_test"]
28
+ )
21
29
 
22
30
  def create_user(self, user_id: int) -> None:
23
31
  """Create a new user in the database."""
@@ -57,4 +65,56 @@ class MongoManager:
57
65
  """Update an order for a user."""
58
66
  self.payments_collection.update_one(
59
67
  {"user_id": user_id, "order_id": order_id}, {"$set": updates}
60
- )
68
+ )
69
+
70
+ def get_user_info(self, update: Update) -> dict:
71
+ """Get user information from the update object."""
72
+ user = update.effective_user
73
+ user_data = self.get_user_data(user.id)
74
+
75
+ return {
76
+ "user_id": user.id,
77
+ "username": user.username,
78
+ "first_name": user.first_name,
79
+ "last_name": user.last_name,
80
+ "lang": user_data.get("language", "en"),
81
+ **user_data,
82
+ }
83
+
84
+ def get_subscription(self, user_id: int) -> dict:
85
+ """Get user's subscription data from the shared subscription database."""
86
+ subscription = self.subscription_collection.find_one({"user_id": user_id})
87
+ if not subscription:
88
+ return {"user_id": user_id, "is_premium": False}
89
+ return subscription
90
+
91
+ def update_subscription(self, user_id: int, updates: dict) -> None:
92
+ """Update user's subscription data in the shared subscription database."""
93
+ self.subscription_collection.update_one(
94
+ {"user_id": user_id}, {"$set": updates}, upsert=True
95
+ )
96
+
97
+ def add_subscription_payment(self, user_id: int, payment_data: dict) -> None:
98
+ """Add a subscription payment record."""
99
+ self.subscription_collection.update_one(
100
+ {"user_id": user_id},
101
+ {
102
+ "$push": {"payments": payment_data},
103
+ "$set": {
104
+ "is_premium": True,
105
+ "premium_expiration": payment_data["expiration_date"],
106
+ "last_payment": payment_data["date"],
107
+ },
108
+ },
109
+ upsert=True,
110
+ )
111
+
112
+ def check_subscription_status(self, user_id: int) -> bool:
113
+ """Check if user has an active subscription."""
114
+ subscription = self.get_subscription(user_id)
115
+
116
+ if not subscription.get("is_premium"):
117
+ return False
118
+
119
+ expiration = datetime.fromisoformat(subscription["premium_expiration"])
120
+ return expiration > datetime.now()
@@ -3,8 +3,6 @@ from logging import getLogger
3
3
  from telegram import Update
4
4
  from telegram.ext import ContextTypes
5
5
  from telegram_libs.translation import t
6
- from telegram_libs.subscription import add_subscription_payment
7
- from telegram_libs.utils import get_user_info
8
6
  from telegram_libs.mongo import MongoManager
9
7
  from telegram_libs.logger import BotLogger
10
8
 
@@ -36,7 +34,7 @@ async def precheckout_handler(update: Update, context: ContextTypes.DEFAULT_TYPE
36
34
 
37
35
  async def successful_payment(update: Update, context: ContextTypes.DEFAULT_TYPE, mongo_manager: MongoManager, bot_logger: BotLogger) -> None:
38
36
  """Handle successful payments"""
39
- user_info = get_user_info(update, mongo_manager)
37
+ user_info = mongo_manager.get_user_info(update)
40
38
  user_id = user_info["user_id"]
41
39
  lang = user_info["lang"]
42
40
  payment_info = update.message.successful_payment
@@ -72,7 +70,7 @@ async def successful_payment(update: Update, context: ContextTypes.DEFAULT_TYPE,
72
70
  current_time = datetime.now()
73
71
 
74
72
  # Add subscription payment to shared subscription database
75
- add_subscription_payment(
73
+ mongo_manager.add_subscription_payment(
76
74
  user_id,
77
75
  {
78
76
  "order_id": payment_info.provider_payment_charge_id,
@@ -1,64 +1,12 @@
1
- from datetime import datetime
1
+ from datetime import datetime, timedelta
2
2
  from telegram import Update, LabeledPrice, InlineKeyboardMarkup, InlineKeyboardButton
3
3
  from telegram.ext import ContextTypes
4
- from telegram_libs.constants import SUBSCRIPTION_DB_NAME, DEBUG, BOTS_AMOUNT
4
+ from telegram_libs.constants import BOTS_AMOUNT, DEBUG
5
5
  from telegram_libs.mongo import MongoManager
6
- from telegram_libs.utils import get_user_info
7
6
  from telegram_libs.translation import t
8
7
  from telegram_libs.logger import BotLogger
9
8
 
10
9
 
11
- # Define the subscription database and collection
12
- mongo_manager_instance = MongoManager(mongo_database_name=SUBSCRIPTION_DB_NAME)
13
- subscription_collection = (
14
- mongo_manager_instance.client[SUBSCRIPTION_DB_NAME]["subscriptions"]
15
- if not DEBUG
16
- else mongo_manager_instance.client[SUBSCRIPTION_DB_NAME]["subscriptions_test"]
17
- )
18
-
19
-
20
- def get_subscription(user_id: int) -> dict:
21
- """Get user's subscription data from the shared subscription database."""
22
- subscription = subscription_collection.find_one({"user_id": user_id})
23
- if not subscription:
24
- return {"user_id": user_id, "is_premium": False}
25
- return subscription
26
-
27
-
28
- def update_subscription(user_id: int, updates: dict) -> None:
29
- """Update user's subscription data in the shared subscription database."""
30
- subscription_collection.update_one(
31
- {"user_id": user_id}, {"$set": updates}, upsert=True
32
- )
33
-
34
-
35
- def add_subscription_payment(user_id: int, payment_data: dict) -> None:
36
- """Add a subscription payment record."""
37
- subscription_collection.update_one(
38
- {"user_id": user_id},
39
- {
40
- "$push": {"payments": payment_data},
41
- "$set": {
42
- "is_premium": True,
43
- "premium_expiration": payment_data["expiration_date"],
44
- "last_payment": payment_data["date"],
45
- },
46
- },
47
- upsert=True,
48
- )
49
-
50
-
51
- def check_subscription_status(user_id: int) -> bool:
52
- """Check if user has an active subscription."""
53
- subscription = get_subscription(user_id)
54
-
55
- if not subscription.get("is_premium"):
56
- return False
57
-
58
- expiration = datetime.fromisoformat(subscription["premium_expiration"])
59
- return expiration > datetime.now()
60
-
61
-
62
10
  async def get_subscription_keyboard(update: Update, lang: str) -> InlineKeyboardMarkup:
63
11
  """Get subscription keyboard
64
12
 
@@ -149,7 +97,7 @@ async def subscribe_command(
149
97
  update: Update, context: ContextTypes.DEFAULT_TYPE, mongo_manager: MongoManager, bot_logger: BotLogger
150
98
  ) -> None:
151
99
  """Show subscription options"""
152
- user_info = get_user_info(update, mongo_manager)
100
+ user_info = mongo_manager.get_user_info(update)
153
101
  user_id = user_info["user_id"]
154
102
  lang = user_info["lang"]
155
103
  bot_name = context.bot.name
@@ -166,11 +114,11 @@ async def check_subscription_command(
166
114
  update: Update, context: ContextTypes.DEFAULT_TYPE, mongo_manager: MongoManager
167
115
  ):
168
116
  """Check user's subscription status"""
169
- user_info = get_user_info(update, mongo_manager)
117
+ user_info = mongo_manager.get_user_info(update)
170
118
  user_id = user_info["user_id"]
171
119
  lang = user_info["lang"]
172
120
 
173
- subscription = get_subscription(user_id)
121
+ subscription = mongo_manager.get_subscription(user_id)
174
122
  if subscription.get("is_premium"):
175
123
  expiration = datetime.fromisoformat(subscription["premium_expiration"])
176
124
  remaining = (expiration - datetime.now()).days
@@ -183,7 +131,7 @@ async def check_subscription_command(
183
131
  )
184
132
  )
185
133
  else:
186
- update_subscription(user_id, {"is_premium": False})
134
+ mongo_manager.update_subscription(user_id, {"is_premium": False})
187
135
  await update.message.reply_text(t("subscription.expired", lang))
188
136
  else:
189
137
  await update.message.reply_text(t("subscription.none", lang))
@@ -0,0 +1,117 @@
1
+ from logging import basicConfig, getLogger, INFO
2
+ from datetime import datetime
3
+ from telegram import (
4
+ InlineKeyboardButton,
5
+ InlineKeyboardMarkup,
6
+ )
7
+ from telegram import Update
8
+ from telegram.ext import ContextTypes
9
+ from telegram_libs.constants import BOTS, BOTS_AMOUNT
10
+ from telegram_libs.translation import t
11
+ from telegram_libs.mongo import MongoManager
12
+ from telegram_libs.logger import BotLogger
13
+
14
+
15
+
16
+ basicConfig(
17
+ format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=INFO
18
+ )
19
+ logger = getLogger(__name__)
20
+
21
+
22
+ async def get_subscription_keyboard(update: Update, lang: str) -> InlineKeyboardMarkup:
23
+ """Get subscription keyboard
24
+
25
+ Args:
26
+ update (Update): Update object
27
+ lang (str): Language code
28
+
29
+ Returns:
30
+ InlineKeyboardMarkup: Inline keyboard markup
31
+ """
32
+ await update.message.reply_text(
33
+ t("subscription.info", lang, common=True).format(BOTS_AMOUNT - 1)
34
+ )
35
+ return [
36
+ [
37
+ InlineKeyboardButton(
38
+ t("subscription.plans.1month", lang, common=True), callback_data="sub_1month"
39
+ ),
40
+ InlineKeyboardButton(
41
+ t("subscription.plans.3months", lang, common=True), callback_data="sub_3months"
42
+ ),
43
+ ],
44
+ [
45
+ InlineKeyboardButton(
46
+ t("subscription.plans.1year", lang, common=True), callback_data="sub_1year"
47
+ ),
48
+ ],
49
+ ]
50
+
51
+
52
+ async def more_bots_list_command(update: Update, context: ContextTypes.DEFAULT_TYPE, bot_logger: BotLogger) -> None:
53
+ user_id = update.effective_user.id
54
+ bot_name = context.bot.name
55
+ bot_logger.log_action(user_id, "more_bots_list_command", bot_name)
56
+ message = "Here is the list of all bots:\n\n"
57
+ bots_list = "\n".join(
58
+ f"- <a href='{url}'>{name}</a>" for url, name in BOTS.items()
59
+ )
60
+ message += bots_list
61
+ await update.message.reply_text(message, disable_web_page_preview=True, parse_mode='HTML')
62
+
63
+
64
+ class RateLimitManager:
65
+ """Rate limit manager to handle user rate limits."""
66
+
67
+ def __init__(self, mongo_manager: MongoManager, rate_limit: int = 5):
68
+ self.mongo_manager = mongo_manager
69
+ self.rate_limit = rate_limit
70
+
71
+ def check_limit(self, user_id: int) -> tuple[bool, dict]:
72
+ """Check if user has exceeded the daily rate limit."""
73
+ # Get today's date and reset time to midnight
74
+ today = datetime.now().date()
75
+
76
+ # If last action date is not today, reset the counter
77
+ user_data = self.mongo_manager.get_user_data(user_id)
78
+ last_action_date_str = user_data.get("last_action_date")
79
+ if last_action_date_str:
80
+ last_action_date = datetime.fromisoformat(last_action_date_str).date()
81
+ if last_action_date != today:
82
+ self.mongo_manager.update_user_data(
83
+ user_id,
84
+ {
85
+ "actions_today": 0,
86
+ "last_action_date": datetime.now().isoformat(),
87
+ },
88
+ )
89
+ return True, user_data
90
+
91
+ # Check if user has exceeded the limit
92
+ actions_today = user_data.get("actions_today", 0)
93
+ if actions_today >= self.rate_limit:
94
+ return False, user_data
95
+
96
+ return True, user_data
97
+
98
+ def check_and_increment(self, user_id: int) -> bool:
99
+ """Check if user can perform an action and increment the count if allowed."""
100
+ if self.mongo_manager.check_subscription_status(user_id):
101
+ return True
102
+
103
+ can_perform, user_data = self.check_limit(user_id)
104
+ if can_perform:
105
+ self.increment_action_count(user_id, user_data)
106
+ return True
107
+ return False
108
+
109
+ def increment_action_count(self, user_id: int, user_data: dict = None) -> None:
110
+ """Increment the daily action count for the user."""
111
+ if user_data is None:
112
+ user_data = self.mongo_manager.get_user_data(user_id)
113
+ current_actions = user_data.get("actions_today", 0)
114
+ self.mongo_manager.update_user_data(
115
+ user_id,
116
+ {"actions_today": current_actions + 1, "last_action_date": datetime.now().isoformat()},
117
+ )
@@ -1,74 +0,0 @@
1
- from logging import basicConfig, getLogger, INFO
2
- from telegram import (
3
- InlineKeyboardButton,
4
- InlineKeyboardMarkup,
5
- )
6
- from telegram import Update
7
- from telegram.ext import ContextTypes
8
- from telegram_libs.constants import BOTS, BOTS_AMOUNT
9
- from telegram_libs.translation import t
10
- from telegram_libs.mongo import MongoManager
11
- from telegram_libs.logger import BotLogger
12
-
13
-
14
- basicConfig(
15
- format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=INFO
16
- )
17
- logger = getLogger(__name__)
18
-
19
-
20
- async def get_subscription_keyboard(update: Update, lang: str) -> InlineKeyboardMarkup:
21
- """Get subscription keyboard
22
-
23
- Args:
24
- update (Update): Update object
25
- lang (str): Language code
26
-
27
- Returns:
28
- InlineKeyboardMarkup: Inline keyboard markup
29
- """
30
- await update.message.reply_text(
31
- t("subscription.info", lang, common=True).format(BOTS_AMOUNT - 1)
32
- )
33
- return [
34
- [
35
- InlineKeyboardButton(
36
- t("subscription.plans.1month", lang, common=True), callback_data="sub_1month"
37
- ),
38
- InlineKeyboardButton(
39
- t("subscription.plans.3months", lang, common=True), callback_data="sub_3months"
40
- ),
41
- ],
42
- [
43
- InlineKeyboardButton(
44
- t("subscription.plans.1year", lang, common=True), callback_data="sub_1year"
45
- ),
46
- ],
47
- ]
48
-
49
-
50
- async def more_bots_list_command(update: Update, context: ContextTypes.DEFAULT_TYPE, bot_logger: BotLogger) -> None:
51
- user_id = update.effective_user.id
52
- bot_name = context.bot.name
53
- bot_logger.log_action(user_id, "more_bots_list_command", bot_name)
54
- message = "Here is the list of all bots:\n\n"
55
- bots_list = "\n".join(
56
- f"- <a href='{url}'>{name}</a>" for url, name in BOTS.items()
57
- )
58
- message += bots_list
59
- await update.message.reply_text(message, disable_web_page_preview=True, parse_mode='HTML')
60
-
61
-
62
- def get_user_info(update: Update, mongo_manager: MongoManager) -> dict:
63
- """Get user information from the update object."""
64
- user = update.effective_user
65
- user_data = mongo_manager.get_user_data(user.id)
66
-
67
- return {
68
- "user_id": user.id,
69
- "username": user.username,
70
- "first_name": user.first_name,
71
- "last_name": user.last_name,
72
- "lang": user_data.get("language", "en"),
73
- **user_data,
74
- }
File without changes
File without changes