RubigramClient 1.6.4__py3-none-any.whl → 1.6.5__py3-none-any.whl

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.

Potentially problematic release.


This version of RubigramClient might be problematic. Click here for more details.

rubigram/__init__.py CHANGED
@@ -2,5 +2,6 @@ from .network import Network
2
2
  from .method import Method
3
3
  from .client import Client
4
4
  from .state import StateManager
5
- from . import models
6
- from . import filters
5
+ from . import enums
6
+ from . import filters
7
+ from . import types
rubigram/client.py CHANGED
@@ -1,13 +1,16 @@
1
1
  from typing import Optional, Callable, Literal, Union
2
- from rubigram.models import Update, InlineMessage
2
+ from rubigram.types import Update, InlineMessage
3
3
  from rubigram.method import Method
4
4
  from rubigram.filters import Filter
5
5
  from rubigram.state import StateManager
6
6
  from datetime import datetime
7
7
  from aiohttp import web
8
8
  import asyncio
9
+ import logging
9
10
 
10
11
 
12
+ logging.basicConfig(format=("%(levelname)s | %(message)s"))
13
+
11
14
 
12
15
  class Client(Method):
13
16
  def __init__(
@@ -96,7 +99,7 @@ class Client(Method):
96
99
  await self.dispatch(update, "message")
97
100
  self.offset_id = get_updates.next_offset_id
98
101
  except Exception as error:
99
- print(f"ERROR : {error}")
102
+ logging.error(error)
100
103
  finally:
101
104
  await self.stop()
102
105
 
@@ -122,5 +125,6 @@ class Client(Method):
122
125
  else:
123
126
  try:
124
127
  asyncio.run(self.runner())
128
+ except KeyboardInterrupt:pass
125
129
  except Exception as error:
126
- print(f"ERROR : {error}")
130
+ logging.error(error)
rubigram/enums.py ADDED
@@ -0,0 +1,129 @@
1
+ from enum import Enum
2
+
3
+
4
+ class ChatType(str, Enum):
5
+ User = "User"
6
+ Bot = "Bot"
7
+ Group = "Group"
8
+ Channel = "Channel"
9
+
10
+
11
+ class ForwardedFrom(str, Enum):
12
+ User = "User"
13
+ Bot = "Bot"
14
+ Channel = "Channel"
15
+
16
+
17
+ class PaymentStatus(str, Enum):
18
+ Paid = "Paid"
19
+ NotPaid = "NotPaid"
20
+
21
+
22
+ class PollStatus(str, Enum):
23
+ Open = "Open"
24
+ Closed = "Closed"
25
+
26
+
27
+ class LiveLocationStatus(str, Enum):
28
+ Stopped = "Stopped"
29
+ Live = "Live"
30
+
31
+
32
+ class ButtonSelectionType(str, Enum):
33
+ TextOnly = "TextOnly"
34
+ TextImgThu = "TextImgThu"
35
+ TextImgBig = "TextImgBig"
36
+
37
+
38
+ class ButtonSelectionSearch(str, Enum):
39
+ Local = "Local"
40
+ Api = "Api"
41
+
42
+
43
+ class ButtonSelectionGet(str, Enum):
44
+ Local = "Local"
45
+ Api = "Api"
46
+
47
+
48
+ class ButtonCalendarType(str, Enum):
49
+ DatePersian = "DatePersian"
50
+ DateGregorian = "DateGregorian"
51
+
52
+
53
+ class ButtonTextboxTypeKeypad(str, Enum):
54
+ String = "String"
55
+ Number = "Number"
56
+
57
+
58
+ class ButtonTextboxTypeLine(str, Enum):
59
+ SingleLine = "SingleLine"
60
+ MultiLine = "MultiLine"
61
+
62
+
63
+ class ButtonLocationType(str, Enum):
64
+ Picker = "Picker"
65
+ View = "View"
66
+
67
+
68
+ class ButtonLinkType(str, Enum):
69
+ joinchannel = "joinchannel"
70
+ url = "url"
71
+
72
+ class MessageSender(str, Enum):
73
+ User = "User"
74
+ Bot = "Bot"
75
+
76
+
77
+ class UpdateType(str, Enum):
78
+ UpdatedMessage = "UpdatedMessage"
79
+ NewMessage = "NewMessage"
80
+ RemovedMessage = "RemovedMessage"
81
+ StartedBot = "StartedBot"
82
+ StoppedBot = "StoppedBot"
83
+ UpdatedPayment = "UpdatedPayment"
84
+
85
+
86
+ class ChatKeypadType(str, Enum):
87
+ New = "New"
88
+ Remove = "Remove"
89
+
90
+
91
+ class UpdateEndpointType(str, Enum):
92
+ ReceiveUpdate = "ReceiveUpdate"
93
+ ReceiveInlineMessage = "ReceiveInlineMessage"
94
+ ReceiveQuery = "ReceiveQuery"
95
+ GetSelectionItem = "GetSelectionItem"
96
+ SearchSelectionItems = "SearchSelectionItems"
97
+
98
+
99
+ class ButtonType(str, Enum):
100
+ Simple = "Simple"
101
+ Selection = "Selection"
102
+ Calendar = "Calendar"
103
+ NumberPicker = "NumberPicker"
104
+ StringPicker = "StringPicker"
105
+ Location = "Location"
106
+ Payment = "Payment"
107
+ CameraImage = "CameraImage"
108
+ CameraVideo = "CameraVideo"
109
+ GalleryImage = "GalleryImage"
110
+ GalleryVideo = "GalleryVideo"
111
+ File = "File"
112
+ Audio = "Audio"
113
+ RecordAudio = "RecordAudio"
114
+ MyPhoneNumber = "MyPhoneNumber"
115
+ MyLocation = "MyLocation"
116
+ Textbox = "Textbox"
117
+ Link = "Link"
118
+ AskMyPhoneNumber = "AskMyPhoneNumber"
119
+ AskLocation = "AskLocation"
120
+ Barcode = "Barcode"
121
+
122
+
123
+ class FileType(str, Enum):
124
+ File = "File"
125
+ Image = "Image"
126
+ Video = "Video"
127
+ Gif = "Gif"
128
+ Music = "Music"
129
+ Voice = "Voice"
rubigram/filters.py CHANGED
@@ -1,4 +1,4 @@
1
- from rubigram.models import Update, InlineMessage
1
+ from rubigram.types import Update, InlineMessage
2
2
  from typing import Union
3
3
  import re
4
4
 
@@ -108,36 +108,45 @@ forward_channel = Filter(FORWARD_CHANNEL)
108
108
 
109
109
 
110
110
  class state(Filter):
111
- def __init__(self, states: Union[str, list[str]]):
112
- self.states = states
111
+ def __init__(self, state: Union[str, list[str]]):
112
+ self.states = state if isinstance(state, list) else [state]
113
113
  super().__init__(self.filter)
114
114
 
115
115
  async def filter(self, update: Union[Update, InlineMessage]):
116
- states = self.states if isinstance(self.states, list) else [self.states]
117
- user = await update.client.state.get_state(update.chat_id)
118
- return user.lower() in states if user else False
116
+ user_state = await update.client.state.get_state(update.chat_id)
117
+ return user_state in self.states
119
118
 
120
119
 
121
120
  class command(Filter):
122
- def __init__(self, cmd: Union[str, list[str]], prefix: str = "/"):
123
- self.cmd = cmd
124
- self.prefix = prefix
121
+ def __init__(self, command: Union[str, list[str]], prefix: Union[str, list[str]] = "/", case_sensitive: bool = False):
122
+ self.commands = [c if case_sensitive else c.lower() for c in (command if isinstance(command, list) else [command])]
123
+ self.prefixs = prefix if isinstance(prefix, list) else [prefix]
124
+ self.cmds = [p + c for p in self.prefixs for c in self.commands]
125
+ self.case_sensitive = case_sensitive
125
126
  super().__init__(self.filter)
126
127
 
127
128
  async def filter(self, update: Update):
128
- commands = self.cmd if isinstance(self.cmd, list) else [self.cmd]
129
- text = ""
130
- if isinstance(update, Update):
131
- if update.type == "NewMessage":
132
- text = update.new_message.text
133
- elif update.type == "UpdatedMessage":
134
- text = update.updated_message.text
135
- if text:
136
- for cmd in commands:
137
- if text.lower().startswith(self.prefix + cmd.lower()):
138
- return True
139
- return False
129
+ if isinstance(update, Update) and update.type == "NewMessage":
130
+ text = update.new_message.text or ""
131
+ text = text if self.case_sensitive else text.lower()
132
+ return any(text.startswith(cmd) for cmd in self.cmds)
133
+ return False
134
+
140
135
 
136
+ class button(Filter):
137
+ def __init__(self, button_id: Union[str, list[str]], prefix: Union[str, list[str]] = "", case_sensitive: bool = False):
138
+ self.button_ids = [btn_id if case_sensitive else btn_id.lower() for btn_id in (button_id if isinstance(button_id, list) else [button_id])]
139
+ self.prefixs = prefix if isinstance(prefix, list) else [prefix]
140
+ self.btn_ids = [p + b for p in self.prefixs for b in self.button_ids]
141
+ self.case_sensitive = case_sensitive
142
+ super().__init__(self.filter)
143
+
144
+ async def filter(self, update: InlineMessage):
145
+ if isinstance(update, InlineMessage):
146
+ text = update.text or ""
147
+ text = text if self.case_sensitive else text.lower()
148
+ return any(text.startswith(btn) for btn in self.btn_ids)
149
+ return False
141
150
 
142
151
  class regex(Filter):
143
152
  def __init__(self, pattern: str):
@@ -166,19 +175,4 @@ class chat(Filter):
166
175
 
167
176
  async def filter(self, update: Union[Update, InlineMessage]):
168
177
  chat_ids = self.chat_id if isinstance(self.chat_id, list) else [self.chat_id]
169
- return update.chat_id in chat_ids
170
-
171
-
172
- class button(Filter):
173
- def __init__(self, button_id: Union[str, list[str]], prefix: str = ""):
174
- self.button_id = button_id
175
- self.prefix = prefix
176
- super().__init__(self.filter)
177
-
178
- async def filter(self, update: InlineMessage):
179
- if isinstance(update, InlineMessage):
180
- button_ids = self.button_id if isinstance(self.button_id, list) else [self.button_id]
181
- for btn_id in button_ids:
182
- if update.aux_data.button_id.startswith(self.prefix + btn_id):
183
- return True
184
- return False
178
+ return update.chat_id in chat_ids
rubigram/method.py CHANGED
@@ -1,12 +1,16 @@
1
1
  from .network import Network
2
- from typing import Literal, Optional
3
- from rubigram.models import Bot, Chat, Keypad, MessageId, Updates
2
+ from . import enums
3
+ from typing import Optional
4
+ from rubigram.types import Bot, Chat, Keypad, MessageId, Updates, BotCommand
4
5
 
5
6
 
6
7
  class Method(Network):
7
8
  def __init__(self, token: str):
8
9
  super().__init__(token)
9
-
10
+
11
+ def clean_data(self, data: dict):
12
+ return {key: value for key, value in data.items() if value is not None}
13
+
10
14
  async def get_me(self) -> "Bot":
11
15
  response = await self.request("getMe", {})
12
16
  return Bot.from_dict(response["bot"])
@@ -23,11 +27,11 @@ class Method(Network):
23
27
  response = await self.request("getFile", {"file_id": file_id})
24
28
  return response["download_url"]
25
29
 
26
- async def set_command(self, command: list):
27
- response = await self.request("setCommands", {"bot_commands": command})
30
+ async def set_command(self, commands: list[BotCommand]):
31
+ response = await self.request("setCommands", {"bot_commands": [command.to_dict() for command in commands]})
28
32
  return response
29
33
 
30
- async def update_bot_endpoint(self, url: str, type: Literal["ReceiveUpdate", "ReceiveInlineMessage", "ReceiveQuery", "GetSelectionItem", "SearchSelectionItems"]):
34
+ async def update_bot_endpoint(self, url: str, type: enums.UpdateEndpointType):
31
35
  response = await self.request("updateBotEndpoints", {"url": url, "type": type})
32
36
  return response
33
37
 
@@ -49,7 +53,10 @@ class Method(Network):
49
53
  async def forward_message(self, from_chat_id: str, message_id: str, to_chat_id: str, disable_notification: bool = False) -> "MessageId":
50
54
  data = {"from_chat_id": from_chat_id, "message_id": message_id, "to_chat_id": to_chat_id, "disable_notification": disable_notification}
51
55
  response = await self.request("forwardMessage", data)
52
- return MessageId.from_dict(response)
56
+ message = MessageId.from_dict(response)
57
+ message.chat_id = to_chat_id
58
+ message.client = self
59
+ return message
53
60
 
54
61
  async def send_message(
55
62
  self,
@@ -57,7 +64,7 @@ class Method(Network):
57
64
  text: str,
58
65
  chat_keypad: Keypad = None,
59
66
  inline_keypad: Keypad = None,
60
- chat_keypad_type: Literal["New", "Remove"] = None,
67
+ chat_keypad_type: Optional[enums.ChatKeypadType] = None,
61
68
  disable_notification: bool = None,
62
69
  reply_to_message_id=None
63
70
  ) -> "MessageId":
@@ -70,9 +77,11 @@ class Method(Network):
70
77
  "disable_notification": disable_notification,
71
78
  "reply_to_message_id": reply_to_message_id
72
79
  }
73
- data = {key: value for key, value in data.items() if value is not None}
74
- response = await self.request("sendMessage", data)
75
- return MessageId.from_dict(response)
80
+ response = await self.request("sendMessage", self.clean_data(data))
81
+ message = MessageId.from_dict(response)
82
+ message.chat_id = chat_id
83
+ message.client = self
84
+ return message
76
85
 
77
86
  async def send_poll(
78
87
  self,
@@ -81,9 +90,9 @@ class Method(Network):
81
90
  options: list[str],
82
91
  chat_keypad: Keypad = None,
83
92
  inline_keypad: Keypad = None,
93
+ chat_keypad_type: Optional[enums.ChatKeypadType] = None,
84
94
  disable_notification: bool = False,
85
95
  reply_to_message_id: str = None,
86
- chat_keypad_type: Literal["New", "Remove"] = None
87
96
  ) -> "MessageId":
88
97
  data = {
89
98
  "chat_id": chat_id,
@@ -95,9 +104,11 @@ class Method(Network):
95
104
  "reply_to_message_id": reply_to_message_id,
96
105
  "chat_keypad_type": chat_keypad_type
97
106
  }
98
- data = {key: value for key, value in data.items() if value is not None}
99
- response = await self.request("sendPoll", data)
100
- return MessageId.from_dict(response)
107
+ response = await self.request("sendPoll", self.clean_data(data))
108
+ message = MessageId.from_dict(response)
109
+ message.chat_id = chat_id
110
+ message.client = self
111
+ return message
101
112
 
102
113
  async def send_location(
103
114
  self,
@@ -106,9 +117,9 @@ class Method(Network):
106
117
  longitude: str,
107
118
  chat_keypad: Keypad = None,
108
119
  inline_keypad: Keypad = None,
120
+ chat_keypad_type: Optional[enums.ChatKeypadType] = None,
109
121
  disable_notification: bool = False,
110
122
  reply_to_message_id: str = None,
111
- chat_keypad_type: Literal["New", "Remove"] = None
112
123
  ) -> "MessageId":
113
124
  data = {
114
125
  "chat_id": chat_id,
@@ -120,9 +131,11 @@ class Method(Network):
120
131
  "reply_to_message_id": reply_to_message_id,
121
132
  "chat_keypad_type": chat_keypad_type
122
133
  }
123
- data = {key: value for key, value in data.items() if value is not None}
124
- response = await self.request("sendLocation", data)
125
- return MessageId.from_dict(response)
134
+ response = await self.request("sendLocation", self.clean_data(data))
135
+ message = MessageId.from_dict(response)
136
+ message.chat_id = chat_id
137
+ message.client = self
138
+ return message
126
139
 
127
140
  async def send_contact(
128
141
  self,
@@ -132,9 +145,9 @@ class Method(Network):
132
145
  phone_number: str,
133
146
  chat_keypad: Keypad = None,
134
147
  inline_keypad: Keypad = None,
148
+ chat_keypad_type: Optional[enums.ChatKeypadType] = None,
135
149
  disable_notification: bool = False,
136
150
  reply_to_message_id: str = None,
137
- chat_keypad_type: Literal["New", "Remove"] = None
138
151
  ) -> "MessageId":
139
152
  data = {
140
153
  "chat_id": chat_id,
@@ -147,9 +160,11 @@ class Method(Network):
147
160
  "reply_to_message_id": reply_to_message_id,
148
161
  "chat_keypad_type": chat_keypad_type
149
162
  }
150
- data = {key: value for key, value in data.items() if value is not None}
151
- response = await self.request("sendContact", data)
152
- return MessageId.from_dict(response)
163
+ response = await self.request("sendContact", self.clean_data(data))
164
+ message = MessageId.from_dict(response)
165
+ message.chat_id = chat_id
166
+ message.client = self
167
+ return message
153
168
 
154
169
  async def send_sticker(
155
170
  self,
@@ -157,9 +172,9 @@ class Method(Network):
157
172
  sticker_id: str,
158
173
  chat_keypad: Keypad = None,
159
174
  inline_keypad: Keypad = None,
175
+ chat_keypad_type: Optional[enums.ChatKeypadType] = None,
160
176
  disable_notification: bool = False,
161
177
  reply_to_message_id: str = None,
162
- chat_keypad_type: Literal["New", "Remove"] = None,
163
178
  ) -> "MessageId":
164
179
  data = {
165
180
  "chat_id": chat_id,
@@ -170,9 +185,11 @@ class Method(Network):
170
185
  "reply_to_message_id": reply_to_message_id,
171
186
  "chat_keypad_type": chat_keypad_type
172
187
  }
173
- data = {key: value for key, value in data.items() if value is not None}
174
- response = await self.request("sendSticker", data)
175
- return MessageId.from_dict(response)
188
+ response = await self.request("sendSticker", self.clean_data(data))
189
+ message = MessageId.from_dict(response)
190
+ message.chat_id = chat_id
191
+ message.client = self
192
+ return message
176
193
 
177
194
  async def request_send_file(self, type: str):
178
195
  response = await self.request("requestSendFile", {"type": type})
@@ -194,10 +211,10 @@ class Method(Network):
194
211
  file: str,
195
212
  file_name: str,
196
213
  caption: str = None,
197
- type: Literal["File", "Image", "Voice", "Music", "Gif", "Video"] = "File",
214
+ type: enums.FileType = enums.FileType.File,
198
215
  chat_keypad: Keypad = None,
199
216
  inline_keypad: Keypad = None,
200
- chat_keypad_type: Literal["New", "Remove"] = None,
217
+ chat_keypad_type: Optional[enums.ChatKeypadType] = None,
201
218
  disable_notification: bool = False,
202
219
  reply_to_message_id: str = None,
203
220
  ) -> "MessageId":
@@ -212,9 +229,12 @@ class Method(Network):
212
229
  "reply_to_message_id": reply_to_message_id,
213
230
  "chat_keypad_type": chat_keypad_type,
214
231
  }
215
- data = {key: value for key, value in data.items() if value is not None}
216
- response = await self.request("sendFile", data)
217
- return MessageId.from_dict(response)
232
+ response = await self.request("sendFile", self.clean_data(data))
233
+ message = MessageId.from_dict(response)
234
+ message.chat_id = chat_id
235
+ message.file_id = file_id
236
+ message.client = self
237
+ return message
218
238
 
219
239
  async def send_document(self, chat_id: str, document: str, name: str, caption: str = None, **kwargs):
220
240
  return await self.send_file(chat_id, document, name, caption, "File", **kwargs)
rubigram/state.py CHANGED
@@ -1,98 +1,35 @@
1
- # from aiosqlite import connect as conn, Connection
2
- # from typing import Optional
1
+ from typing import Any
3
2
 
4
-
5
- # class ManageDB:
6
- # def __init__(self, database: str):
7
- # self.database = database
8
- # self.connection: Optional[Connection] = None
9
-
10
-
11
- # async def connect(self):
12
- # if not self.connection:
13
- # self.connection = await conn(self.database)
14
-
15
-
16
- # async def disconnect(self):
17
- # if self.connection:
18
- # await self.connection.close()
19
- # self.connection = None
20
-
21
-
22
- # async def create(self):
23
- # await self.connect()
24
- # await self.connection.execute("""CREATE TABLE IF NOT EXISTS states (user_id TEXT PRIMARY KEY, state TEXT)""")
25
- # await self.connection.execute("CREATE TABLE IF NOT EXISTS user_data (user_id TEXT, var TEXT, res TEXT, PRIMARY KEY (user_id, var))")
26
- # await self.connection.commit()
27
-
28
-
29
- # async def set_state(self, user_id: str, state: str) -> None:
30
- # await self.connect()
31
- # await self.connection.execute("INSERT INTO states (user_id, state) VALUES (?, ?) ON CONFLICT(user_id) DO UPDATE SET state=excluded.state ", (user_id, str(state)))
32
- # await self.connection.commit()
33
-
34
-
35
- # async def get_state(self, user_id: str) -> Optional[str]:
36
- # await self.connect()
37
- # async with self.connection.execute("SELECT state FROM states WHERE user_id = ?", (user_id,)) as cursor:
38
- # data = await cursor.fetchone()
39
- # return data[0] if data else None
40
-
41
-
42
- # async def remove_state(self, user_id: str):
43
- # await self.connect()
44
- # await self.connection.execute("DELETE FROM states WHERE user_id = ?", (user_id,))
45
- # await self.connection.commit()
46
-
47
-
48
- # async def set_data(self, user_id: str, key: str, value: str):
49
- # await self.connect()
50
- # await self.connection.execute("INSERT OR REPLACE INTO user_data (user_id, var, res) VALUES (?, ?, ?)", (user_id, key, str(value)))
51
- # await self.connection.commit()
52
-
53
-
54
- # async def get_data(self, user_id: str, key: str = None):
55
- # await self.connect()
56
- # if key:
57
- # async with self.connection.execute("SELECT res FROM user_data WHERE user_id = ? AND var = ?", (user_id, key)) as cursor:
58
- # data = await cursor.fetchone()
59
- # return data[0] if data else None
60
- # else:
61
- # async with self.connection.execute("SELECT var, res FROM user_data WHERE user_id = ?", (user_id,)) as cursor:
62
- # data = await cursor.fetchall()
63
- # return {k: v for k, v in data} if data else {}
64
-
65
-
66
- # async def remove_data(self, user_id: str, key: str = None):
67
- # await self.connect()
68
- # if key:
69
- # await self.connection.execute("DELETE FROM user_data WHERE user_id = ? AND var = ?", (user_id, key))
70
- # else:
71
- # await self.connection.execute("DELETE FROM user_data WHERE user_id = ?", (user_id,))
72
- # await self.connection.commit()
73
-
74
-
75
3
  class StateManager:
76
4
  def __init__(self):
77
- self.DATA = {}
78
- self.STATE = {}
5
+ self.DATA: dict[str, dict[str, Any]] = {}
6
+ self.STATE: dict[str, str] = {}
7
+
79
8
 
80
9
  async def set_state(self, user_id: str, state: str):
81
10
  self.STATE[user_id] = state
82
11
 
12
+
83
13
  async def get_state(self, user_id: str):
84
14
  return self.STATE.get(user_id)
15
+
85
16
 
86
17
  async def remove_state(self, user_id: str):
87
18
  self.STATE.pop(user_id, None)
19
+
88
20
 
89
21
  async def set_data(self, user_id: str, **data):
90
22
  if user_id not in self.DATA:
91
23
  self.DATA[user_id] = {}
92
24
  self.DATA[user_id].update(data)
25
+
93
26
 
94
27
  async def get_data(self, user_id: str, key: str = None):
95
- return self.DATA.get(user_id, {}).get(key) if key else self.DATA.get(user_id, {})
28
+ data = self.DATA.get(user_id, {})
29
+ return data.get(key) if key else data
30
+
96
31
 
97
32
  async def remove_data(self, user_id: str, key: str = None):
98
- self.DATA.get(user_id, {}).pop(key, None) if key else self.DATA.pop(user_id, None)
33
+ if key:
34
+ return self.DATA.get(user_id, {}).pop(key, None)
35
+ return self.DATA.pop(user_id, None)