PyRubikaBotAPI 0.0.1__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.
- pyrubikabotapi-0.0.1.dist-info/LICENCE +21 -0
- pyrubikabotapi-0.0.1.dist-info/METADATA +56 -0
- pyrubikabotapi-0.0.1.dist-info/RECORD +8 -0
- pyrubikabotapi-0.0.1.dist-info/WHEEL +5 -0
- pyrubikabotapi-0.0.1.dist-info/top_level.txt +1 -0
- rubibot/__init__.py +837 -0
- rubibot/helper.py +109 -0
- rubibot/types.py +242 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Alireza Sadeghian
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: PyRubikaBotAPI
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: A Python library to send messages and files via Eitaa API
|
|
5
|
+
Home-page: https://github.com/alireza-sadeghian/EitaaSender
|
|
6
|
+
Author: Alireza Sadeghian
|
|
7
|
+
Author-email: alireza.amid110@gmail.com
|
|
8
|
+
License: MIT
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Requires-Python: >=3.10
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENCE
|
|
15
|
+
Requires-Dist: requests
|
|
16
|
+
|
|
17
|
+
# PyRubikaBotAPI
|
|
18
|
+
|
|
19
|
+
یک کتابخانه برای ساخت رباتهای روبیکا با پایتون
|
|
20
|
+
|
|
21
|
+
## نصب
|
|
22
|
+
```bash
|
|
23
|
+
pip install PyRubikaBotAPI
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## مثال ساده
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
from rubibot import RubiBot
|
|
31
|
+
|
|
32
|
+
bot = RubiBot("YOUR_TOKEN")
|
|
33
|
+
|
|
34
|
+
@bot.message_handler(commands=["start"])
|
|
35
|
+
def start(message):
|
|
36
|
+
bot.send_message(message.chat_id, "سلام! خوش آمدید!")
|
|
37
|
+
|
|
38
|
+
bot.polling()
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## مثال پیشرفته تر
|
|
42
|
+
```py
|
|
43
|
+
@bot.message_handler(content_types=['file'])
|
|
44
|
+
def handle(msg):
|
|
45
|
+
file_id = msg.file.id
|
|
46
|
+
file_url = bot.get_file(file_id)
|
|
47
|
+
file = bot.download_file(file_url)
|
|
48
|
+
with open('file.format', 'wb') as f
|
|
49
|
+
f.write(file)
|
|
50
|
+
# استفاده از فایل ارسالی
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## مستندات:
|
|
54
|
+
|
|
55
|
+
Rubika: https://rubika.ir/pyrubikabotapi
|
|
56
|
+
GitHub: https://github.com/alireza-sadeghian/PyRubikaBotAPI
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
rubibot/__init__.py,sha256=zTKcgvIX-ZpyfuNMfw3gUq7lBsVJFgjjzrhHBfX5te0,31518
|
|
2
|
+
rubibot/helper.py,sha256=al6HhY6tYOWfw4JjBbpLYgXDDDP8yJ4094P1M_ez4Dc,4586
|
|
3
|
+
rubibot/types.py,sha256=i1Pvzr1Jy9JOpxgLFc64V6grZMOZ00r0abU-QE8RLeo,6767
|
|
4
|
+
pyrubikabotapi-0.0.1.dist-info/LICENCE,sha256=mds18XAwr8xSo4pIhwMfB9spNkvrm5KKmLl_KIKQky4,1095
|
|
5
|
+
pyrubikabotapi-0.0.1.dist-info/METADATA,sha256=nRj-WRGD_IMrtln4uadd50gJvRJMkgLqaloLEHrdEaw,1415
|
|
6
|
+
pyrubikabotapi-0.0.1.dist-info/WHEEL,sha256=WnJ8fYhv8N4SYVK2lLYNI6N0kVATA7b0piVUNvqIIJE,91
|
|
7
|
+
pyrubikabotapi-0.0.1.dist-info/top_level.txt,sha256=TAhaSYH8WLX-dab28vY3_Wif-mrUw1pmS_RJOFaiogE,8
|
|
8
|
+
pyrubikabotapi-0.0.1.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
rubibot
|
rubibot/__init__.py
ADDED
|
@@ -0,0 +1,837 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
import time
|
|
4
|
+
import requests
|
|
5
|
+
from typing import Any, List, Optional
|
|
6
|
+
from rubibot import helper
|
|
7
|
+
from rubibot import types
|
|
8
|
+
|
|
9
|
+
# In the name of Allah
|
|
10
|
+
# Developer: Alireza Sadeghian
|
|
11
|
+
# URL: https://github.com/alireza-sadeghian/PyRubikaBotAPI
|
|
12
|
+
# Channel: https://rubika.ir/pyrubikabotapi
|
|
13
|
+
# Faghat Heydar Amiralmomenin ast
|
|
14
|
+
|
|
15
|
+
"""
|
|
16
|
+
Welcome to PyRubikaBotAPI
|
|
17
|
+
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
class RubiBot:
|
|
21
|
+
"""
|
|
22
|
+
This is a main class for RubiBot
|
|
23
|
+
Using this class, you can define handlers for your Rubika robots and respond appropriately to updates using methods.
|
|
24
|
+
|
|
25
|
+
for example:
|
|
26
|
+
|
|
27
|
+
import rubibot
|
|
28
|
+
bot = rubibot.RubiBot('TOKEN') # You need to get this token from @BotFather
|
|
29
|
+
# Now you can define handlers for your bot and use methods.
|
|
30
|
+
|
|
31
|
+
See more examples on our Rubika channel.
|
|
32
|
+
https://rubika.ir/PyRubikaBotAPI
|
|
33
|
+
|
|
34
|
+
توضیحات فارسی:
|
|
35
|
+
این اصلی ترین کلاس برای ربات شماست
|
|
36
|
+
برای استفاده از امکانات کتابخانه یک شیء از این کلاس ایجاد کنید
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
def __init__(self, token):
|
|
42
|
+
self.TOKEN = token
|
|
43
|
+
self.BASE_URL = f"https://botapi.rubika.ir/v3/{self.TOKEN}"
|
|
44
|
+
self._message_handlers = []
|
|
45
|
+
self.OFFSET_FILE = "noi.json" # noi: n → next, o → offset, i → id
|
|
46
|
+
|
|
47
|
+
def message_handler(
|
|
48
|
+
self,
|
|
49
|
+
commands: Optional[List[str]]=None,
|
|
50
|
+
content_types: Optional[List[str]]=None
|
|
51
|
+
):
|
|
52
|
+
"""
|
|
53
|
+
It is responsible for managing messages received from the user.
|
|
54
|
+
It handles all types of messages such as text, video, Voice, Poll, etc.
|
|
55
|
+
As a parameter to the decorator function, it passes :class:`rubibot.types.Message` object.
|
|
56
|
+
All message handlers are stored and executed in the order in which they are written.
|
|
57
|
+
|
|
58
|
+
for example:
|
|
59
|
+
|
|
60
|
+
bot = RubiBot('token')
|
|
61
|
+
|
|
62
|
+
# Handle /start command
|
|
63
|
+
@bot.message_handler(commands=['start'])
|
|
64
|
+
def start(message: rubibot.types.Message):
|
|
65
|
+
bot.send_message(message.chat_id, "Hello from RubiBot!")
|
|
66
|
+
|
|
67
|
+
# Handle all sticker messages
|
|
68
|
+
@bot.message_handler(content_types=['sticker'])
|
|
69
|
+
def sticker_handler(message):
|
|
70
|
+
bot.send_file(message.chat.id, message.sticker.file.id, "This is your sticker image")
|
|
71
|
+
|
|
72
|
+
# Handle all sent messages of text type
|
|
73
|
+
@bot.message_handler()
|
|
74
|
+
def handler(message):
|
|
75
|
+
bot.reply_to(chat_id=message, text=message.text)
|
|
76
|
+
|
|
77
|
+
:param commands: list of commands
|
|
78
|
+
|
|
79
|
+
:param content_types: Supported message content types ↓
|
|
80
|
+
['text', 'file', 'location', 'sticker', 'contact', 'poll']
|
|
81
|
+
|
|
82
|
+
:return: decorated function
|
|
83
|
+
|
|
84
|
+
توضیحات فارسی:
|
|
85
|
+
این تابع پیام های دریافتی و بروزرسانی هارا مدیریت می کند
|
|
86
|
+
برای استفاده از این تابع یک دکوریتور از آن ایجاد کنید و تابع مورد نظر
|
|
87
|
+
برای هندل کردن پیام هارا به عنوان تابع ورودی برای دکوریتور تعریف کنید
|
|
88
|
+
|
|
89
|
+
این دکوریتور یک شیء از کلاس مسیج
|
|
90
|
+
از پیام دریافتی توسط کاربر را به عنوان ورودی به تابعی که مشخص کرده اید می دهد
|
|
91
|
+
|
|
92
|
+
هندلر های پیام به ترتیب افزوده شدن اجرا می شوند
|
|
93
|
+
و بیش از یک هندلر برای یک پیام اجرا نمی شود!
|
|
94
|
+
|
|
95
|
+
پارامتر ها:
|
|
96
|
+
commands: لیست دستوراتی که میخواهید هندلر شما فقط آنها را هندل کند
|
|
97
|
+
content_types: فیلتر کردن نوع محتوا برای هندل شدن در هندلر شما
|
|
98
|
+
|
|
99
|
+
جهت دیدن مثال های بیشتر و دریافت سورس کد های
|
|
100
|
+
آماده و آزمایشی به کانال ما در سوپراپلیکیشن روبیکا سر بزنید
|
|
101
|
+
https://rubika.ir/PyRubikaBotAPI
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
LISTOFCONTENTTYPES = ['text', 'file', 'location', 'sticker', 'contact', 'poll']
|
|
106
|
+
|
|
107
|
+
if isinstance(commands, str):
|
|
108
|
+
commands = [commands]
|
|
109
|
+
|
|
110
|
+
if content_types is None:
|
|
111
|
+
content_types = ['text']
|
|
112
|
+
|
|
113
|
+
if isinstance(content_types, str):
|
|
114
|
+
content_types = [content_types]
|
|
115
|
+
|
|
116
|
+
for ct in content_types:
|
|
117
|
+
if ct not in LISTOFCONTENTTYPES:
|
|
118
|
+
raise ValueError(f"{ct} is not supported, supported values: {LISTOFCONTENTTYPES}")
|
|
119
|
+
|
|
120
|
+
def decorator(handler):
|
|
121
|
+
self._message_handlers.append({
|
|
122
|
+
"handler": handler,
|
|
123
|
+
"filters": {
|
|
124
|
+
"commands": commands,
|
|
125
|
+
"content_types": content_types
|
|
126
|
+
}
|
|
127
|
+
})
|
|
128
|
+
return handler
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
return decorator
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def get_updates(self,offset=None, limit=10):
|
|
135
|
+
"""
|
|
136
|
+
for get updates from rubika
|
|
137
|
+
|
|
138
|
+
:param offset: If you want to receive only new updates when receiving updates, put the value `next_offset_id` from the previous request here. If you leave nothing, by default all previous updates will be sent in this request.
|
|
139
|
+
|
|
140
|
+
:param limit: Limit on number of updates, Default: 10
|
|
141
|
+
|
|
142
|
+
:return: Tuple of(updates, next_ofsset_id) # for polling
|
|
143
|
+
|
|
144
|
+
"""
|
|
145
|
+
if limit > 100 or limit < 1:
|
|
146
|
+
raise ValueError("The limit cannot be greater than 100 and less than 1.")
|
|
147
|
+
|
|
148
|
+
params = {"limit": limit}
|
|
149
|
+
if offset:
|
|
150
|
+
params["offset_id"] = offset
|
|
151
|
+
try:
|
|
152
|
+
res = requests.post(f"{self.BASE_URL}/getUpdates", json=params, timeout=15)
|
|
153
|
+
except Exception as e:
|
|
154
|
+
raise Exception("Error: {}".format(e))
|
|
155
|
+
data = res.json()
|
|
156
|
+
if not res or res.status_code !=200:
|
|
157
|
+
raise Exception("Error in receiving updates: {}".format(data.get("status")))
|
|
158
|
+
|
|
159
|
+
updates = []
|
|
160
|
+
for upd in data.get("data", {}).get("updates"):
|
|
161
|
+
updates.append(types.Update(json.dumps(upd)))
|
|
162
|
+
return updates, data.get("data").get("next_offset_id")
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def __load_offset(self):
|
|
166
|
+
if not os.path.exists(self.OFFSET_FILE):
|
|
167
|
+
self.__save_offset(None)
|
|
168
|
+
return None
|
|
169
|
+
|
|
170
|
+
with open(self.OFFSET_FILE, "r", encoding="utf-8") as f:
|
|
171
|
+
return json.load(f).get("next_offset_id")
|
|
172
|
+
|
|
173
|
+
def __save_offset(self, offset_id):
|
|
174
|
+
with open(self.OFFSET_FILE, "w", encoding="utf-8") as f:
|
|
175
|
+
json.dump(
|
|
176
|
+
{"next_offset_id": offset_id},
|
|
177
|
+
f,
|
|
178
|
+
ensure_ascii=False,
|
|
179
|
+
indent=4
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
def polling(self, t: int = 2, limit=10):
|
|
183
|
+
"""
|
|
184
|
+
This function receives updates from Rubica indefinitely and repeatedly at specified intervals and manages them using written handlers.
|
|
185
|
+
|
|
186
|
+
**Warning: This method is only for testing the bot in local mode and should not be used as the main method for receiving and handling updates. This method is not optimal and if you do this, a large number of requests will be sent to Rubika and you will be limited.
|
|
187
|
+
|
|
188
|
+
:param t: The time interval between requests
|
|
189
|
+
|
|
190
|
+
:param limit: Limit the number of updates received per request
|
|
191
|
+
|
|
192
|
+
:return:
|
|
193
|
+
|
|
194
|
+
توضیحات فارسی:
|
|
195
|
+
در ابتدا دقت کنید که از این تابع فقط و فقط به صورت آزمایشی و برای
|
|
196
|
+
تست کردن ربات خود استفاده کنید و به هیچ عنوان برای دریافت آپدیت ها
|
|
197
|
+
به صورت دائم از این متود استفاده نکنید و به جای آن
|
|
198
|
+
از وبهوک استفاده کنید
|
|
199
|
+
|
|
200
|
+
این متد را در آخر سورس کد خود فراخوانی کنید تا بروزرسانی ها را در بازه
|
|
201
|
+
های زمانی مشخص از روبیکا دریافت کند و توسط هندلر های ایجاد شده توسط شما هندل کند
|
|
202
|
+
"""
|
|
203
|
+
next_offset_id = self.__load_offset()
|
|
204
|
+
|
|
205
|
+
while True:
|
|
206
|
+
updates, next_offset_id = self.get_updates(offset=next_offset_id, limit=limit)
|
|
207
|
+
if not next_offset_id:
|
|
208
|
+
next_offset_id = self.__load_offset()
|
|
209
|
+
self.__save_offset(next_offset_id)
|
|
210
|
+
self.process_new_updates(updates)
|
|
211
|
+
time.sleep(t)
|
|
212
|
+
|
|
213
|
+
def send_message(
|
|
214
|
+
self,chat_id,text,
|
|
215
|
+
chat_keypad=None,
|
|
216
|
+
inline_keypad=None,
|
|
217
|
+
disable_notification=False,
|
|
218
|
+
_reply_to_message_id = None
|
|
219
|
+
):
|
|
220
|
+
|
|
221
|
+
"""
|
|
222
|
+
Use this method to send text messages.
|
|
223
|
+
|
|
224
|
+
**Warning: Do not send more than about 4096 characters each message
|
|
225
|
+
|
|
226
|
+
:param chat_id: The unique identifier of a chat, such as a private conversation or a group/Channel
|
|
227
|
+
:type chat_id: :obj:`str`
|
|
228
|
+
|
|
229
|
+
:param text: Text to send
|
|
230
|
+
:type text: :obj:`str`
|
|
231
|
+
|
|
232
|
+
:param chat_keypad: Buttons at the bottom of the page that are sent with the message
|
|
233
|
+
:type chat_keypad: :obj:`rubibot.types.ChatKeypad` or :obj:`rubibot.types.ChatKeypadRemove`
|
|
234
|
+
|
|
235
|
+
:param inline_keypad: In-text buttons that appear below the message text
|
|
236
|
+
:type inline_keypad: :obj:`rubibot.types.InlineKeypad`
|
|
237
|
+
|
|
238
|
+
:param disable_notification: Send a message with silent notification
|
|
239
|
+
:type disable_notification: :obj:`bool`
|
|
240
|
+
|
|
241
|
+
:param _reply_to_message_id: It is used to reply to messages,
|
|
242
|
+
but you should use :method:`rubibot.RubiBot.reply_to()` to reply to plain text.
|
|
243
|
+
|
|
244
|
+
:return: On success, the send message id is returned
|
|
245
|
+
:rtype: :obj:`str`
|
|
246
|
+
|
|
247
|
+
توضیحات فارسی:
|
|
248
|
+
از این متود برای ارسال پیام متنی به یک چت مشخص استفاده کنید
|
|
249
|
+
طول پیام شما نباید بیشتر از 4096 کاراکتر باشد و گرنه خطا دریافت می کنید
|
|
250
|
+
|
|
251
|
+
پارامتر ها:
|
|
252
|
+
|
|
253
|
+
chat_id: شناسه چت مورد نظر برای ارسال پیام متنی
|
|
254
|
+
text: پیام مورد نظر
|
|
255
|
+
chat_keypad: اگر میخواهید همراه با پیام، دکمه هایی در پایین صفحه برای کاربر ارسال شود
|
|
256
|
+
ابتدا کی پد خود را با استفاده از:
|
|
257
|
+
`rubibot.types.ChatKeypad`
|
|
258
|
+
ایجاد کنید و آن را در اینجا قرار دهید
|
|
259
|
+
inline_keypad: اگر میخواهید دکمه هایی در زیر پیام ارسالی ایجاد شود ابتدا کی پد خود را با استفاده از:
|
|
260
|
+
`rubibot.types.InlineKeypad`
|
|
261
|
+
ایجاد کنید و سپس آن را اینجا قرار دهید
|
|
262
|
+
disable_notification: اگر میخواهید پیام شما با اعلان بدون صدا ارسال شود این مقدار را برابر
|
|
263
|
+
`True` قرار دهید
|
|
264
|
+
"""
|
|
265
|
+
|
|
266
|
+
data = {"chat_id": chat_id, "text": text}
|
|
267
|
+
|
|
268
|
+
if chat_keypad:
|
|
269
|
+
type_ = chat_keypad.type
|
|
270
|
+
data["chat_keypad_type"] = type_
|
|
271
|
+
if type_ != "Remove":
|
|
272
|
+
data["chat_keypad"] = chat_keypad._get_data()
|
|
273
|
+
|
|
274
|
+
if inline_keypad:
|
|
275
|
+
data["inline_keypad"] = inline_keypad._get_data()
|
|
276
|
+
|
|
277
|
+
if disable_notification:
|
|
278
|
+
data["disable_notification"] = True
|
|
279
|
+
|
|
280
|
+
if _reply_to_message_id:
|
|
281
|
+
data["reply_to_message_id"] = _reply_to_message_id
|
|
282
|
+
|
|
283
|
+
method_name = 'sendMessage'
|
|
284
|
+
return helper._make_request(self.TOKEN, method_name, data)
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def reply_to(
|
|
289
|
+
self, message: types.Message,
|
|
290
|
+
text, chat_keypad=None,
|
|
291
|
+
inline_keypad=None,
|
|
292
|
+
disable_notification=False
|
|
293
|
+
):
|
|
294
|
+
"""
|
|
295
|
+
Send a text message as a reply
|
|
296
|
+
This function is for your convenience in sending text messages as replies, but to send other types of messages as replies, you must use the `reply_to_message_id` value available in those methods.
|
|
297
|
+
|
|
298
|
+
:param message: message to reply
|
|
299
|
+
:type message: :obj:`rubibot.types.Message`
|
|
300
|
+
|
|
301
|
+
The rest of the values are the same as those defined in `rubibot.RubiBot.send_message()`.
|
|
302
|
+
|
|
303
|
+
:return: On success, the send message id is returned
|
|
304
|
+
:rtype: :obj:`str`
|
|
305
|
+
|
|
306
|
+
توضیحات فارسی:
|
|
307
|
+
برای ارسال پیام های متنی و ریپلای کردن آن ها از این متود استفاده کنید
|
|
308
|
+
"""
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
return self.send_message(
|
|
312
|
+
message.chat_id, text,
|
|
313
|
+
chat_keypad, inline_keypad,
|
|
314
|
+
disable_notification,
|
|
315
|
+
message.message_id
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def get_me(self):
|
|
320
|
+
"""
|
|
321
|
+
It does not receive any input and returns information about the bot.
|
|
322
|
+
|
|
323
|
+
:return: Bot information
|
|
324
|
+
:rtype: :obj:`rubibot.types.Bot`
|
|
325
|
+
"""
|
|
326
|
+
res = requests.post("{}/getMe".format(self.BASE_URL))
|
|
327
|
+
r = res.json().get("data")
|
|
328
|
+
bot = r.get("bot")
|
|
329
|
+
bot_id = bot.get("bot_id")
|
|
330
|
+
bot_title = bot.get("bot_title")
|
|
331
|
+
avatar = bot.get("avatar", {})
|
|
332
|
+
avatar_id = avatar.get("file_id")
|
|
333
|
+
avatar_name = avatar.get("file_name")
|
|
334
|
+
avatar_size = avatar.get("size")
|
|
335
|
+
avatar_ = types.File(avatar_id, avatar_name, avatar_size)
|
|
336
|
+
description = bot.get("description")
|
|
337
|
+
username = bot.get("user_name")
|
|
338
|
+
start_msg = bot.get("start_message")
|
|
339
|
+
share_url = bot.get("share_url")
|
|
340
|
+
|
|
341
|
+
return types.Bot(bot_id, bot_title, avatar_, description, username, start_msg, share_url)
|
|
342
|
+
|
|
343
|
+
def send_poll(self, chat_id: str, poll: types.ChatPoll):
|
|
344
|
+
"""
|
|
345
|
+
Send a poll to a specific chat
|
|
346
|
+
|
|
347
|
+
:param chat_id: Desired chat ID
|
|
348
|
+
:type chat_id: :obj:`str`
|
|
349
|
+
|
|
350
|
+
:param poll: Desired poll
|
|
351
|
+
:type poll: :obj:`rubibot.types.ChatPoll`
|
|
352
|
+
|
|
353
|
+
:return: On success, the send message id is returned
|
|
354
|
+
:rtype: :obj:`str`
|
|
355
|
+
"""
|
|
356
|
+
data = poll._get_data()
|
|
357
|
+
data["chat_id"] = chat_id
|
|
358
|
+
method_name = 'sendPoll'
|
|
359
|
+
return helper._make_request(self.TOKEN, method_name, data)
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def send_contact(
|
|
363
|
+
self, chat_id: str, first_name: str, last_name: str,
|
|
364
|
+
phone_number: str, chat_keypad: types.ChatKeypad= None,
|
|
365
|
+
inline_keypad: types.InlineKeypad = None, disable_notification: bool = False,
|
|
366
|
+
reply_to_message_id: str = None
|
|
367
|
+
):
|
|
368
|
+
"""
|
|
369
|
+
Send a contact to a specific chat
|
|
370
|
+
|
|
371
|
+
:param chat_id: Desired Chat ID
|
|
372
|
+
:type chat_id: :obj:`str`
|
|
373
|
+
|
|
374
|
+
:param first_name: Contact's first name
|
|
375
|
+
:type first_name: :obj:`str`
|
|
376
|
+
|
|
377
|
+
:param last_name: Contact's last name
|
|
378
|
+
:type last_name: :obj:`str`
|
|
379
|
+
|
|
380
|
+
:param phone_number: Contact's phone number
|
|
381
|
+
:type phone_number: :obj:`str`
|
|
382
|
+
|
|
383
|
+
The rest of the values are the same as those defined in `rubibot.RubiBot.send_message()`.
|
|
384
|
+
|
|
385
|
+
:return: On success, the send message id is returned
|
|
386
|
+
:rtype: :obj:`str`
|
|
387
|
+
|
|
388
|
+
"""
|
|
389
|
+
data = {
|
|
390
|
+
"chat_id": chat_id,
|
|
391
|
+
"first_name": first_name,
|
|
392
|
+
"last_name": last_name,
|
|
393
|
+
"phone_number": phone_number
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if chat_keypad:
|
|
397
|
+
type_ = chat_keypad.type
|
|
398
|
+
data["chat_keypad_type"] = type_
|
|
399
|
+
data["chat_keypad"] = chat_keypad._get_data()
|
|
400
|
+
|
|
401
|
+
if inline_keypad:
|
|
402
|
+
data["inline_keypad"] = inline_keypad._get_data()
|
|
403
|
+
|
|
404
|
+
if disable_notification:
|
|
405
|
+
data["disable_notification"] = True
|
|
406
|
+
|
|
407
|
+
if reply_to_message_id:
|
|
408
|
+
data["reply_to_message_id"] = reply_to_message_id
|
|
409
|
+
|
|
410
|
+
method_name = 'sendContact'
|
|
411
|
+
return helper._make_request(self.TOKEN, method_name, data)
|
|
412
|
+
|
|
413
|
+
def get_chat(self, chat_id: str):
|
|
414
|
+
"""
|
|
415
|
+
Get the desired chat information, including name and username.
|
|
416
|
+
|
|
417
|
+
:param chat_id: Desired Chat ID
|
|
418
|
+
:type chat_id: :obj:`str`
|
|
419
|
+
|
|
420
|
+
:return: Chat information
|
|
421
|
+
:rtype: :obj:`rubibot.types.Chat`
|
|
422
|
+
|
|
423
|
+
"""
|
|
424
|
+
data = {
|
|
425
|
+
"chat_id": chat_id
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
res = requests.post("{}/getChat".format(self.BASE_URL), json=data)
|
|
429
|
+
data = res.json().get("data")
|
|
430
|
+
if data:
|
|
431
|
+
chat = data.get("chat")
|
|
432
|
+
chat_id = chat.get("chat_id")
|
|
433
|
+
chat_type = chat.get("chat_type")
|
|
434
|
+
user_id = chat.get("user_id")
|
|
435
|
+
first_name = chat.get("first_name")
|
|
436
|
+
last_name = chat.get("last_name")
|
|
437
|
+
title = chat.get("title")
|
|
438
|
+
username = chat.get("username")
|
|
439
|
+
return types.Chat(chat_id, chat_type, user_id, first_name, last_name, title, username)
|
|
440
|
+
else:
|
|
441
|
+
raise Exception("Rubika Error: {}".format(res.json().get("status")))
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
def forward(self, from_chat_id: str, to_chat_id: str, message_id: str, disable_notification: bool = False):
|
|
446
|
+
"""
|
|
447
|
+
forward a message
|
|
448
|
+
|
|
449
|
+
"""
|
|
450
|
+
data = {
|
|
451
|
+
"from_chat_id": from_chat_id,
|
|
452
|
+
"message_id": message_id,
|
|
453
|
+
"to_chat_id": to_chat_id,
|
|
454
|
+
"disable_notification": disable_notification
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
method_name = 'forwardMessage'
|
|
458
|
+
return helper._make_request(self.TOKEN, method_name, data)
|
|
459
|
+
|
|
460
|
+
def edit_message(
|
|
461
|
+
self, chat_id: str, message_id: str,
|
|
462
|
+
new_text: str, new_inline_keypad: types.InlineKeypad = None
|
|
463
|
+
):
|
|
464
|
+
"""
|
|
465
|
+
Edit a message
|
|
466
|
+
|
|
467
|
+
توضیحات فارسی:
|
|
468
|
+
|
|
469
|
+
پارامتر های قابل توضیح:
|
|
470
|
+
new_text: متنی که میخواهید جایگزین متن قبلی شود
|
|
471
|
+
new_inline_keypad: اگر میخواهید دکمه های درون متنی خود را نیز تغییر بدهید، مقدار جدیدی برای این ورودی قرار دهید
|
|
472
|
+
"""
|
|
473
|
+
|
|
474
|
+
data = {
|
|
475
|
+
"chat_id": chat_id,
|
|
476
|
+
"message_id": message_id,
|
|
477
|
+
"text": new_text
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
method_name = 'editMessageText'
|
|
481
|
+
helper._make_request(self.TOKEN, method_name, data)
|
|
482
|
+
|
|
483
|
+
if new_inline_keypad:
|
|
484
|
+
data.pop("text")
|
|
485
|
+
data["inline_keypad"] = new_inline_keypad._get_data()
|
|
486
|
+
|
|
487
|
+
method_name = 'editMessageKeypad'
|
|
488
|
+
return helper._make_request(self.TOKEN, method_name, data)
|
|
489
|
+
|
|
490
|
+
return True
|
|
491
|
+
|
|
492
|
+
def delete_message(self, chat_id: str, message_id: str):
|
|
493
|
+
"""
|
|
494
|
+
Delete a message
|
|
495
|
+
|
|
496
|
+
"""
|
|
497
|
+
data = {
|
|
498
|
+
"chat_id": chat_id,
|
|
499
|
+
"message_id": message_id
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
method_name = 'deleteMessage'
|
|
503
|
+
return helper._make_request(self.TOKEN, method_name, data)
|
|
504
|
+
|
|
505
|
+
def set_commands(self, *args):
|
|
506
|
+
"""
|
|
507
|
+
Setting commands for the bot, this way any new user who logs into your bot will see the commands you register here in the user interface and can use them.
|
|
508
|
+
|
|
509
|
+
:param args: Desired commands
|
|
510
|
+
:type args: :obj:`rubibot.types.BotCommand`
|
|
511
|
+
|
|
512
|
+
:return: On success, return True
|
|
513
|
+
:rtype: :obj:`bool`
|
|
514
|
+
|
|
515
|
+
"""
|
|
516
|
+
data = {
|
|
517
|
+
"bot_commands": []
|
|
518
|
+
}
|
|
519
|
+
for arg in args:
|
|
520
|
+
data["bot_commands"].append(arg._get_data())
|
|
521
|
+
|
|
522
|
+
method_name = 'setCommands'
|
|
523
|
+
return helper._make_request(self.TOKEN, method_name, data)
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
def edit_chat_keypad(self, chat_id: str, chat_keypad):
|
|
527
|
+
"""
|
|
528
|
+
Edit ChatKeypad
|
|
529
|
+
|
|
530
|
+
"""
|
|
531
|
+
data = {"chat_id": chat_id}
|
|
532
|
+
type_ = chat_keypad._get_type()
|
|
533
|
+
data["chat_keypad_type"] = type_
|
|
534
|
+
if type_ != "Remove":
|
|
535
|
+
data["chat_keypad"] = chat_keypad._get_data()
|
|
536
|
+
|
|
537
|
+
method_name = 'editChatKeypad'
|
|
538
|
+
return helper._make_request(self.TOKEN, method_name, data)
|
|
539
|
+
|
|
540
|
+
def set_webhook(self, url, type="ReceiveUpdate"):
|
|
541
|
+
data = {"url": url, "type": type}
|
|
542
|
+
method_name = 'updateBotEndpoints'
|
|
543
|
+
return helper._make_request(self.TOKEN, method_name, data)
|
|
544
|
+
|
|
545
|
+
|
|
546
|
+
def get_file(self, file_id):
|
|
547
|
+
"""
|
|
548
|
+
Get the download address of a file by file ID
|
|
549
|
+
|
|
550
|
+
for example:
|
|
551
|
+
|
|
552
|
+
download_url = bot.get_file('file_id')
|
|
553
|
+
file = bot.download_file(download_url)
|
|
554
|
+
# Now the file sent from the user has been downloaded and stored in the file variable.
|
|
555
|
+
|
|
556
|
+
:param file_id: The file id
|
|
557
|
+
:type file_id: :obj:`str`
|
|
558
|
+
|
|
559
|
+
:return: file download url
|
|
560
|
+
:rtype: :obj:`str`
|
|
561
|
+
|
|
562
|
+
توضیحات فارسی:
|
|
563
|
+
آیدی فایل را به عنوان ورودی قرار دهید تا لینک دانلود به شما بازگردانده شود
|
|
564
|
+
با لینک دانلود میتوانید فایل را دانلود کنید
|
|
565
|
+
|
|
566
|
+
"""
|
|
567
|
+
data ={
|
|
568
|
+
"file_id": file_id
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
method_name = 'getFile'
|
|
572
|
+
return helper._make_request(self.TOKEN, method_name, data)
|
|
573
|
+
|
|
574
|
+
def download_file(self, file_url) -> bytes:
|
|
575
|
+
"""
|
|
576
|
+
Download a file by download URL
|
|
577
|
+
|
|
578
|
+
:param file_url: The file url
|
|
579
|
+
:type file_url: :obj:`str`
|
|
580
|
+
|
|
581
|
+
:return: The File
|
|
582
|
+
:rtype: :obj:`bytes`
|
|
583
|
+
|
|
584
|
+
توضیحات فارسی:
|
|
585
|
+
لینک دانلود را به عنوان ورودی قرار دهید تا فایل مورد نظر به عنوان
|
|
586
|
+
خروجی به شما تحویل داده شود
|
|
587
|
+
|
|
588
|
+
"""
|
|
589
|
+
res = requests.get(file_url)
|
|
590
|
+
if res.status_code != 200:
|
|
591
|
+
raise Exception(f"Download File Error")
|
|
592
|
+
return res.content
|
|
593
|
+
|
|
594
|
+
def request_send_file(self, file_type):
|
|
595
|
+
"""
|
|
596
|
+
Request to upload a file to Rubika servers
|
|
597
|
+
|
|
598
|
+
توضیحات فارسی:
|
|
599
|
+
درخواست برای آپلود یک فایل روی سرور های روبیکا
|
|
600
|
+
* به صورت پیشفرض نیازی به استفاده از این متود ندارید
|
|
601
|
+
و برای ارسال فایل به یک چت می توانید از متود های زیر استفاده کنید:
|
|
602
|
+
`rubibot.RubiBot.send_photo`
|
|
603
|
+
`rubibot.RubiBot.send_file`
|
|
604
|
+
و دیگر متود های ارسال فایل
|
|
605
|
+
|
|
606
|
+
"""
|
|
607
|
+
if file_type not in ['File', 'Image', 'Voice', 'Video', 'Music', 'Gif']:
|
|
608
|
+
raise ValueError("Sorry")
|
|
609
|
+
data = {"type": file_type}
|
|
610
|
+
method_name = 'requestSendFile'
|
|
611
|
+
return helper._make_request(self.TOKEN, method_name, data)
|
|
612
|
+
|
|
613
|
+
|
|
614
|
+
def __send_file(
|
|
615
|
+
self, file_type, chat_id, file, text, chat_keypad: types.ChatKeypad, inline_keypad: types.InlineKeypad, reply_to_message_id, disable_notification
|
|
616
|
+
):
|
|
617
|
+
|
|
618
|
+
data = {"chat_id": chat_id, "text": text}
|
|
619
|
+
|
|
620
|
+
if chat_keypad:
|
|
621
|
+
data["chat_keypad_type"] = chat_keypad._get_type()
|
|
622
|
+
data["chat_keypad"] = chat_keypad._get_data()
|
|
623
|
+
if inline_keypad:
|
|
624
|
+
data["inline_keypad"] = inline_keypad._get_data()
|
|
625
|
+
if reply_to_message_id:
|
|
626
|
+
data["reply_to_message_id"] = reply_to_message_id
|
|
627
|
+
if disable_notification:
|
|
628
|
+
data["disable_notification"] = disable_notification
|
|
629
|
+
|
|
630
|
+
upload_url = self.request_send_file(file_type)
|
|
631
|
+
|
|
632
|
+
data["file_id"] = self.upload_file(file, upload_url)
|
|
633
|
+
|
|
634
|
+
method_name = 'sendFile'
|
|
635
|
+
return helper._make_request(self.TOKEN, method_name, data)
|
|
636
|
+
|
|
637
|
+
def _create_file_type(self, file):
|
|
638
|
+
if hasattr(file, "read"):
|
|
639
|
+
return file
|
|
640
|
+
elif isinstance(file, str):
|
|
641
|
+
return open(file, 'rb')
|
|
642
|
+
else:
|
|
643
|
+
raise ValueError('invalid type')
|
|
644
|
+
|
|
645
|
+
def upload_file(self, file, upload_url):
|
|
646
|
+
file = self._create_file_type(file)
|
|
647
|
+
files = {"file": ("file", file)}
|
|
648
|
+
res = requests.post(
|
|
649
|
+
upload_url,
|
|
650
|
+
files=files
|
|
651
|
+
)
|
|
652
|
+
file.close()
|
|
653
|
+
|
|
654
|
+
return helper._chek_result_request(res)
|
|
655
|
+
|
|
656
|
+
def send_photo(
|
|
657
|
+
self, chat_id: str, photo: Any,
|
|
658
|
+
text: str, chat_keypad: Optional[types.ChatKeypad]=None,
|
|
659
|
+
inline_keypad: Optional[types.InlineKeypad]=None,
|
|
660
|
+
reply_to_message_id:Optional[str]=None, disable_notification: bool=False
|
|
661
|
+
):
|
|
662
|
+
"""
|
|
663
|
+
Send an image with a maximum size of 10 MB.
|
|
664
|
+
Supported formats: PNG, JPG, GIF, WEBP
|
|
665
|
+
|
|
666
|
+
:param photo: the photo to send
|
|
667
|
+
:type photo: :obj:`bytes` or File Path in Disk
|
|
668
|
+
|
|
669
|
+
The rest of the values are the same as those defined in `rubibot.RubiBot.send_message()`.
|
|
670
|
+
|
|
671
|
+
:return: On success, the send message id is returned
|
|
672
|
+
:rtype: :obj:`str`
|
|
673
|
+
"""
|
|
674
|
+
|
|
675
|
+
return self.__send_file(
|
|
676
|
+
"Image", chat_id, photo, text, chat_keypad, inline_keypad,
|
|
677
|
+
reply_to_message_id, disable_notification
|
|
678
|
+
)
|
|
679
|
+
|
|
680
|
+
def send_voice(
|
|
681
|
+
self, chat_id: str, voice: Any,
|
|
682
|
+
text: str, chat_keypad: Optional[types.ChatKeypad]=None,
|
|
683
|
+
inline_keypad: Optional[types.InlineKeypad]=None,
|
|
684
|
+
reply_to_message_id:Optional[str]=None, disable_notification: bool=False
|
|
685
|
+
):
|
|
686
|
+
"""
|
|
687
|
+
Send a Voice.
|
|
688
|
+
Supported formats: MP3
|
|
689
|
+
|
|
690
|
+
:param voice: the voice to send
|
|
691
|
+
:type voice: :obj:`bytes` or File Path in Disk
|
|
692
|
+
|
|
693
|
+
The rest of the values are the same as those defined in `rubibot.RubiBot.send_message()`.
|
|
694
|
+
|
|
695
|
+
:return: On success, the send message id is returned
|
|
696
|
+
:rtype: :obj:`str`
|
|
697
|
+
"""
|
|
698
|
+
|
|
699
|
+
return self.__send_file(
|
|
700
|
+
"Voice", chat_id, voice, text, chat_keypad, inline_keypad,
|
|
701
|
+
reply_to_message_id, disable_notification
|
|
702
|
+
)
|
|
703
|
+
|
|
704
|
+
def send_video(
|
|
705
|
+
self, chat_id: str, video: Any,
|
|
706
|
+
text: str, chat_keypad: Optional[types.ChatKeypad]=None,
|
|
707
|
+
inline_keypad: Optional[types.InlineKeypad]=None,
|
|
708
|
+
reply_to_message_id:Optional[str]=None, disable_notification: bool=False
|
|
709
|
+
):
|
|
710
|
+
"""
|
|
711
|
+
Send a Video with a maximum size of 50 MB.
|
|
712
|
+
Supported formats: MP4
|
|
713
|
+
|
|
714
|
+
:param video: the video to send
|
|
715
|
+
:type video: :obj:`bytes` or File Path in Disk
|
|
716
|
+
|
|
717
|
+
The rest of the values are the same as those defined in `rubibot.RubiBot.send_message()`.
|
|
718
|
+
|
|
719
|
+
:return: On success, the send message id is returned
|
|
720
|
+
:rtype: :obj:`str`
|
|
721
|
+
"""
|
|
722
|
+
|
|
723
|
+
return self.__send_file(
|
|
724
|
+
"Video", chat_id, video, text, chat_keypad, inline_keypad,
|
|
725
|
+
reply_to_message_id, disable_notification
|
|
726
|
+
)
|
|
727
|
+
|
|
728
|
+
def send_gif(
|
|
729
|
+
self, chat_id: str, gif: Any,
|
|
730
|
+
text: str, chat_keypad: Optional[types.ChatKeypad]=None,
|
|
731
|
+
inline_keypad: Optional[types.InlineKeypad]=None,
|
|
732
|
+
reply_to_message_id:Optional[str]=None, disable_notification: bool=False
|
|
733
|
+
):
|
|
734
|
+
"""
|
|
735
|
+
Send a Gif that must be without sound.
|
|
736
|
+
Supported formats: MP4
|
|
737
|
+
|
|
738
|
+
:param gif: the gif to send
|
|
739
|
+
:type gif: :obj:`bytes` or File Path in Disk
|
|
740
|
+
|
|
741
|
+
The rest of the values are the same as those defined in `rubibot.RubiBot.send_message()`.
|
|
742
|
+
|
|
743
|
+
:return: On success, the send message id is returned
|
|
744
|
+
:rtype: :obj:`str`
|
|
745
|
+
"""
|
|
746
|
+
|
|
747
|
+
return self.__send_file(
|
|
748
|
+
"Gif", chat_id, gif, text, chat_keypad, inline_keypad,
|
|
749
|
+
reply_to_message_id, disable_notification
|
|
750
|
+
)
|
|
751
|
+
|
|
752
|
+
def send_file(
|
|
753
|
+
self, chat_id: str, file: Any,
|
|
754
|
+
text: str, chat_keypad: Optional[types.ChatKeypad]=None,
|
|
755
|
+
inline_keypad: Optional[types.InlineKeypad]=None,
|
|
756
|
+
reply_to_message_id:Optional[str]=None, disable_notification: bool=False
|
|
757
|
+
):
|
|
758
|
+
"""
|
|
759
|
+
Send an other files with a maximum size of 50MB.
|
|
760
|
+
Supported formats: all
|
|
761
|
+
|
|
762
|
+
:param file: the file to send
|
|
763
|
+
:type file: :obj:`bytes` or File Path in Disk
|
|
764
|
+
|
|
765
|
+
The rest of the values are the same as those defined in `rubibot.RubiBot.send_message()`.
|
|
766
|
+
|
|
767
|
+
:return: On success, the send message id is returned
|
|
768
|
+
:rtype: :obj:`str`
|
|
769
|
+
|
|
770
|
+
"""
|
|
771
|
+
|
|
772
|
+
return self.__send_file(
|
|
773
|
+
"File", chat_id, file, text, chat_keypad, inline_keypad,
|
|
774
|
+
reply_to_message_id, disable_notification
|
|
775
|
+
)
|
|
776
|
+
|
|
777
|
+
|
|
778
|
+
def _test_message_handler(self, handler, message):
|
|
779
|
+
filters = handler["filters"]
|
|
780
|
+
if filters["commands"]:
|
|
781
|
+
if not message.text:
|
|
782
|
+
return False
|
|
783
|
+
cmd = message.text.split()[0][1:]
|
|
784
|
+
if cmd not in filters["commands"]:
|
|
785
|
+
return False
|
|
786
|
+
|
|
787
|
+
if filters["content_types"]:
|
|
788
|
+
if message._get_content_type() not in filters["content_types"]:
|
|
789
|
+
return False
|
|
790
|
+
|
|
791
|
+
return True
|
|
792
|
+
|
|
793
|
+
|
|
794
|
+
def process_new_updates(self, updates: list[types.Update]):
|
|
795
|
+
"""
|
|
796
|
+
Processing new updates
|
|
797
|
+
Usage instructions:
|
|
798
|
+
First convert the received updates into an object of the `rubibot.types.Update` class and pass this object as input to this function
|
|
799
|
+
|
|
800
|
+
Warning: The input to this function must be in the form of a list.
|
|
801
|
+
|
|
802
|
+
for exapmle:
|
|
803
|
+
|
|
804
|
+
update = rubibot.types.Update(myupdate) # Updates received from Webhook
|
|
805
|
+
bot.process_new_updates([update])
|
|
806
|
+
# Now the received update is processed by the handlers you defined. This is one of the most important steps in running your bot.
|
|
807
|
+
|
|
808
|
+
see more example:
|
|
809
|
+
https://github.com/alireza-sadeghian/PyRubikaBotAPI
|
|
810
|
+
|
|
811
|
+
:param updates: Received updates
|
|
812
|
+
:type updates: :obj:`list` of :obj:`rubibot.types.Updates`
|
|
813
|
+
|
|
814
|
+
توضیحات فارسی:
|
|
815
|
+
اگر از پولینگ استفاده می کنید نیازی به این ندارید
|
|
816
|
+
اگر از وبهوک استفاده میکنید، ابتدا جیسون دریافتی را به شیء آپدیت
|
|
817
|
+
از کتابخانه روبی بات تبدیل کنید و سپس آن را به صورت لیست به عنوان ورودی این تابع قرار دهید
|
|
818
|
+
تا با استفاده از هندلر هایی که ثبت کرده اید هندل شود
|
|
819
|
+
"""
|
|
820
|
+
if not isinstance(updates, List):
|
|
821
|
+
raise Exception("Invalid type for updates")
|
|
822
|
+
|
|
823
|
+
for upd in updates:
|
|
824
|
+
|
|
825
|
+
if not isinstance(upd, types.Update):
|
|
826
|
+
raise Exception("Invalid type for update")
|
|
827
|
+
|
|
828
|
+
message = helper._to_message(upd.dict)
|
|
829
|
+
for handler in self._message_handlers:
|
|
830
|
+
if self._test_message_handler(handler, message):
|
|
831
|
+
handler["handler"](message)
|
|
832
|
+
break
|
|
833
|
+
|
|
834
|
+
|
|
835
|
+
|
|
836
|
+
# soon...
|
|
837
|
+
# This is not the end of our work... 😎
|
rubibot/helper.py
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
from rubibot.types import AuxData, Contact, File, ForwardedFrom, Location, Message, Poll, PollStatus, Sticker
|
|
3
|
+
def _to_message(raw) -> Message:
|
|
4
|
+
|
|
5
|
+
type_ = raw.get("type")
|
|
6
|
+
chat_id = raw.get("chat_id")
|
|
7
|
+
# removed_message_id = raw.get("removed_message_id") # In the new update. soon...
|
|
8
|
+
new_message = raw.get("new_message")
|
|
9
|
+
# updated_message = raw.get("updated_message") # In the new update. soon...
|
|
10
|
+
message_id = new_message.get("message_id")
|
|
11
|
+
text = new_message.get("text")
|
|
12
|
+
time = new_message.get("time")
|
|
13
|
+
is_edited : bool = new_message.get("is_edited")
|
|
14
|
+
sender_type = new_message.get("sender_type") # User or Bot
|
|
15
|
+
sender_id = new_message.get("sender_id")
|
|
16
|
+
aux_data = new_message.get("aux_data", {})
|
|
17
|
+
aux = AuxData(aux_data.get("start_id"), aux_data.get("button_id"))
|
|
18
|
+
reply_to_message_id = new_message.get("reply_to_message_id")
|
|
19
|
+
forwarded_from_data = new_message.get("forwarded_from", {})
|
|
20
|
+
type_forward = forwarded_from_data.get("type_from")
|
|
21
|
+
forward_message_id = forwarded_from_data.get("message_id")
|
|
22
|
+
forward_chat_id = forwarded_from_data.get("from_chat_id")
|
|
23
|
+
forward_sender_id = forwarded_from_data.get("from_sender_id")
|
|
24
|
+
forwarded_from = ForwardedFrom(type_forward, forward_message_id, forward_chat_id, forward_sender_id)
|
|
25
|
+
file_data = new_message.get("file", {})
|
|
26
|
+
file_id = file_data.get("file_id")
|
|
27
|
+
file_name = file_data.get("file_name")
|
|
28
|
+
file_size = file_data.get("size")
|
|
29
|
+
file = File(file_id, file_name, file_size)
|
|
30
|
+
location_data = new_message.get("location", {})
|
|
31
|
+
longitude = location_data.get("longitude")
|
|
32
|
+
latitude = location_data.get("latitude")
|
|
33
|
+
location = Location(longitude, latitude)
|
|
34
|
+
sticker_data = new_message.get("sticker", {})
|
|
35
|
+
sticker_id = sticker_data.get("sticker_id")
|
|
36
|
+
sticker_file_data = sticker_data.get("file", {})
|
|
37
|
+
sticker_file_id = sticker_file_data.get("file_id")
|
|
38
|
+
sticker_file_name = sticker_file_data.get("file_name")
|
|
39
|
+
sticker_file_size = sticker_file_data.get("size")
|
|
40
|
+
sticker_file = File(sticker_file_id, sticker_file_name, sticker_file_size)
|
|
41
|
+
sticker_emoji_character = sticker_data.get("emoji_character")
|
|
42
|
+
sticker = Sticker(sticker_id, sticker_file, sticker_emoji_character)
|
|
43
|
+
contact_data = new_message.get("contact_message", {})
|
|
44
|
+
contact_phone_number = contact_data.get("phone_number")
|
|
45
|
+
contact_first_name = contact_data.get("first_name")
|
|
46
|
+
contact_last_name = contact_data.get("last_name")
|
|
47
|
+
contact = Contact(contact_first_name, contact_last_name, contact_phone_number)
|
|
48
|
+
poll_data = new_message.get("poll", {})
|
|
49
|
+
poll_question = poll_data.get("question")
|
|
50
|
+
poll_options = poll_data.get("options")
|
|
51
|
+
poll_status_data = poll_data.get("poll_status", {})
|
|
52
|
+
poll_status_state = poll_status_data.get("state")
|
|
53
|
+
poll_status_selection_index = poll_status_data.get("selection_index")
|
|
54
|
+
poll_status_percent_vote_options = poll_status_data.get("percent_vote_options")
|
|
55
|
+
poll_status_total_vote = poll_status_data.get("total_vote")
|
|
56
|
+
poll_status_show_total_votes: bool = poll_status_data.get("show_total_votes")
|
|
57
|
+
poll_status = PollStatus(
|
|
58
|
+
poll_status_state,
|
|
59
|
+
poll_status_selection_index,
|
|
60
|
+
poll_status_percent_vote_options,
|
|
61
|
+
poll_status_total_vote,
|
|
62
|
+
poll_status_show_total_votes
|
|
63
|
+
)
|
|
64
|
+
poll = Poll(poll_question, poll_options, poll_status)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
message = Message(
|
|
69
|
+
type_,
|
|
70
|
+
chat_id,
|
|
71
|
+
message_id,
|
|
72
|
+
text,
|
|
73
|
+
time,
|
|
74
|
+
is_edited,
|
|
75
|
+
sender_type,
|
|
76
|
+
sender_id,
|
|
77
|
+
aux,
|
|
78
|
+
reply_to_message_id,
|
|
79
|
+
forwarded_from,
|
|
80
|
+
file,
|
|
81
|
+
location,
|
|
82
|
+
sticker,
|
|
83
|
+
contact,
|
|
84
|
+
poll
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
return message
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _chek_result_request(res):
|
|
91
|
+
res = res.json()
|
|
92
|
+
if res["status"] != "OK":
|
|
93
|
+
error = res.get('dev_message', res.get('status'))
|
|
94
|
+
raise Exception("Rubika Error: {}".format(error))
|
|
95
|
+
data = res.get("data", {})
|
|
96
|
+
if data.get('message_id'):
|
|
97
|
+
return data.get('message_id')
|
|
98
|
+
if data.get('file_id'):
|
|
99
|
+
return data.get('file_id')
|
|
100
|
+
if data.get('upload_url'):
|
|
101
|
+
return data.get('upload_url')
|
|
102
|
+
if data.get('download_url'):
|
|
103
|
+
return data.get('download_url')
|
|
104
|
+
return None
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _make_request(token, method_name, data=None):
|
|
108
|
+
result = requests.post("https://botapi.rubika.ir/v3/{0}/{1}".format(token, method_name), json=data)
|
|
109
|
+
return _chek_result_request(result)
|
rubibot/types.py
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Update:
|
|
5
|
+
def __init__(self, update):
|
|
6
|
+
self._update = json.loads(update)
|
|
7
|
+
if self._update.get("update"):
|
|
8
|
+
self.dict = self._update.get("update")
|
|
9
|
+
else:
|
|
10
|
+
self.dict = self._update
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class AuxData:
|
|
14
|
+
def __init__(self, start_id, button_id):
|
|
15
|
+
self.start_id = start_id
|
|
16
|
+
self.button_id = button_id
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class File:
|
|
20
|
+
|
|
21
|
+
def __init__(self, file_id, file_name, size):
|
|
22
|
+
self.id = file_id
|
|
23
|
+
self.name = file_name
|
|
24
|
+
self.size = size
|
|
25
|
+
|
|
26
|
+
class Chat:
|
|
27
|
+
def __init__(self, chat_id, chat_type, user_id, first_name, last_name, title, username):
|
|
28
|
+
self.id = chat_id
|
|
29
|
+
self.type = chat_type
|
|
30
|
+
self.user_id = user_id
|
|
31
|
+
self.first_name = first_name
|
|
32
|
+
self.last_name = last_name
|
|
33
|
+
self.title = title
|
|
34
|
+
self.username = username
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class ForwardedFrom:
|
|
38
|
+
def __init__(self, type_, message_id, chat_id, sender_id):
|
|
39
|
+
self.type = type_
|
|
40
|
+
self.message_id = message_id
|
|
41
|
+
self.chat_id = chat_id
|
|
42
|
+
self.sender_id = sender_id
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class Location:
|
|
47
|
+
def __init__(self, longitude, latitude):
|
|
48
|
+
self.longitude = longitude
|
|
49
|
+
self.latitude = latitude
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class Sticker:
|
|
53
|
+
def __init__(self, sticker_id: str, file: File, emoji_character: str):
|
|
54
|
+
self.id = sticker_id
|
|
55
|
+
self.file = file
|
|
56
|
+
self.emoji_character= emoji_character
|
|
57
|
+
|
|
58
|
+
class Contact:
|
|
59
|
+
def __init__(self, first_name, last_name, phone_number):
|
|
60
|
+
self.first_name = first_name
|
|
61
|
+
self.last_name = last_name
|
|
62
|
+
self.phone_number = phone_number
|
|
63
|
+
|
|
64
|
+
class PollStatus:
|
|
65
|
+
def __init__(self, state, selection_index, percent_vote_options, total_vote, show_total_votes):
|
|
66
|
+
self.state = state
|
|
67
|
+
self.selection_index = selection_index
|
|
68
|
+
self.percent_vote_options = percent_vote_options
|
|
69
|
+
self.total_vote = total_vote
|
|
70
|
+
self.show_total_votes = show_total_votes
|
|
71
|
+
|
|
72
|
+
class Poll:
|
|
73
|
+
def __init__(self, question, options, status: PollStatus):
|
|
74
|
+
self.question = question
|
|
75
|
+
self.options = options
|
|
76
|
+
self.status = status
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class Bot:
|
|
80
|
+
|
|
81
|
+
def __init__(self, bot_id, bot_title, bot_avatar: File, description , username, start_message, share_url):
|
|
82
|
+
self.id = bot_id
|
|
83
|
+
self.title = bot_title
|
|
84
|
+
self.avatar = bot_avatar
|
|
85
|
+
self.description = description
|
|
86
|
+
self.username = username
|
|
87
|
+
self.start_message = start_message
|
|
88
|
+
self.share_url = share_url
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class BotCommand:
|
|
92
|
+
def __init__(self, command: str, description: str):
|
|
93
|
+
self.command = command
|
|
94
|
+
self.description = description
|
|
95
|
+
def _get_data(self):
|
|
96
|
+
return {
|
|
97
|
+
"command": self.command,
|
|
98
|
+
"description": self.description
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class ChatPoll:
|
|
103
|
+
def __init__(self, question):
|
|
104
|
+
self.question = question
|
|
105
|
+
self.options = []
|
|
106
|
+
|
|
107
|
+
def add_options(self, *args: str):
|
|
108
|
+
if len(self.options) < 10:
|
|
109
|
+
if len(args) + len(self.options) > 10:
|
|
110
|
+
raise ValueError("You cannot add more than 10 options!")
|
|
111
|
+
for arg in args:
|
|
112
|
+
self.options.append(arg)
|
|
113
|
+
|
|
114
|
+
def _get_data(self):
|
|
115
|
+
return {
|
|
116
|
+
"question": self.question,
|
|
117
|
+
"options": self.options
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class ChatKeypad:
|
|
123
|
+
"""
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
"""
|
|
127
|
+
|
|
128
|
+
def __init__(self, resize_keyboard=False, on_time_keyboard=False):
|
|
129
|
+
self.keypad_rows = {"rows": []}
|
|
130
|
+
self._data = {"resize_keyboard": resize_keyboard, "on_time_keyboard": on_time_keyboard}
|
|
131
|
+
self.type = "New"
|
|
132
|
+
|
|
133
|
+
def add(self, *keypad_rows):
|
|
134
|
+
for kr in keypad_rows:
|
|
135
|
+
self.keypad_rows["rows"].append(kr._get_data())
|
|
136
|
+
|
|
137
|
+
self._data["rows"] = self.keypad_rows.get("rows")
|
|
138
|
+
def _get_data(self):
|
|
139
|
+
return self._data
|
|
140
|
+
|
|
141
|
+
def _get_type(self):
|
|
142
|
+
return self.type
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
class ChatKeypadRemove:
|
|
146
|
+
def __init__(self):
|
|
147
|
+
self.type = "Remove"
|
|
148
|
+
|
|
149
|
+
def _get_type(self):
|
|
150
|
+
return self.type
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class InlineKeypad:
|
|
154
|
+
|
|
155
|
+
def __init__(
|
|
156
|
+
self,
|
|
157
|
+
resize_keyboard = False,
|
|
158
|
+
on_time_keyboard = False
|
|
159
|
+
): # ): (: /: |:
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
self._data = {"rows": [], "resize_keyboard": resize_keyboard, "on_time_keyboard": on_time_keyboard}
|
|
163
|
+
|
|
164
|
+
def add(self, *args):
|
|
165
|
+
|
|
166
|
+
if not self._data.get("rows"):
|
|
167
|
+
self._data["rows"] = []
|
|
168
|
+
for kr in args:
|
|
169
|
+
self._data["rows"].append(kr._get_data())
|
|
170
|
+
|
|
171
|
+
def _get_data(self):
|
|
172
|
+
return self._data
|
|
173
|
+
|
|
174
|
+
class KeypadRow:
|
|
175
|
+
def __init__(self):
|
|
176
|
+
self._data = {}
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def add(self, *buttons):
|
|
180
|
+
self._data["buttons"] = []
|
|
181
|
+
for btn in buttons:
|
|
182
|
+
self._data["buttons"].append(btn._get_data())
|
|
183
|
+
|
|
184
|
+
def _get_data(self):
|
|
185
|
+
return self._data
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
class KeypadSimpleButton:
|
|
189
|
+
def __init__(self, text, id):
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
self.id = id
|
|
193
|
+
self.text = text
|
|
194
|
+
self.type = "Simple"
|
|
195
|
+
self._data = {}
|
|
196
|
+
|
|
197
|
+
def _set_data(self):
|
|
198
|
+
self._data["id"] = self.id
|
|
199
|
+
self._data["type"] = self.type
|
|
200
|
+
self._data["button_text"] = self.text
|
|
201
|
+
|
|
202
|
+
return self._data
|
|
203
|
+
|
|
204
|
+
def _get_data(self):
|
|
205
|
+
return self._set_data()
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
class Message:
|
|
209
|
+
def __init__(
|
|
210
|
+
self, type_: str, chat_id: str, message_id: str,
|
|
211
|
+
text: str, time: float, is_edited: bool, sender_type: str, sender_id: str,
|
|
212
|
+
aux: AuxData, reply_to_message_id: str, forwarded_from: ForwardedFrom,
|
|
213
|
+
file: File, location: Location, sticker: Sticker, contact: Contact, poll: Poll
|
|
214
|
+
):
|
|
215
|
+
|
|
216
|
+
self.type = type_
|
|
217
|
+
self.chat_id = chat_id
|
|
218
|
+
self.message_id = message_id
|
|
219
|
+
self.text = text
|
|
220
|
+
self.time = time
|
|
221
|
+
self.is_edited = is_edited
|
|
222
|
+
self.sender_type = sender_type
|
|
223
|
+
self.sender_id = sender_id
|
|
224
|
+
self.aux = aux
|
|
225
|
+
self.reply_to_message_id = reply_to_message_id
|
|
226
|
+
self.forwarded_from = forwarded_from
|
|
227
|
+
self.file = file
|
|
228
|
+
self.location = location
|
|
229
|
+
self.sticker = sticker
|
|
230
|
+
self.contact = contact
|
|
231
|
+
self.poll = poll
|
|
232
|
+
|
|
233
|
+
def _get_content_type(self):
|
|
234
|
+
if self.file and self.file.id: return "file"
|
|
235
|
+
if self.location and self.location.latitude is not None: return "location"
|
|
236
|
+
if self.sticker and self.sticker.id: return "sticker"
|
|
237
|
+
if self.contact and self.contact.phone_number: return "contact"
|
|
238
|
+
if self.poll and self.poll.question: return "poll"
|
|
239
|
+
return "text"
|
|
240
|
+
|
|
241
|
+
def __repr__(self):
|
|
242
|
+
return f"<rubibot.types.Message chat={self.chat_id} text={self.text}>"
|