ParsMeet 1.2.0__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.
- parsmeet-1.2.0/PKG-INFO +14 -0
- parsmeet-1.2.0/ParsMeet/__init__.py +7 -0
- parsmeet-1.2.0/ParsMeet/api/__init__.py +2 -0
- parsmeet-1.2.0/ParsMeet/api/auth.py +18 -0
- parsmeet-1.2.0/ParsMeet/api/rooms.py +78 -0
- parsmeet-1.2.0/ParsMeet/bot.py +243 -0
- parsmeet-1.2.0/ParsMeet/client.py +27 -0
- parsmeet-1.2.0/ParsMeet/database.py +33 -0
- parsmeet-1.2.0/ParsMeet/exceptions.py +8 -0
- parsmeet-1.2.0/ParsMeet/models.py +20 -0
- parsmeet-1.2.0/ParsMeet/realtime/__init__.py +1 -0
- parsmeet-1.2.0/ParsMeet/realtime/py.typed +0 -0
- parsmeet-1.2.0/ParsMeet/realtime/socket.py +11 -0
- parsmeet-1.2.0/ParsMeet.egg-info/PKG-INFO +14 -0
- parsmeet-1.2.0/ParsMeet.egg-info/SOURCES.txt +20 -0
- parsmeet-1.2.0/ParsMeet.egg-info/dependency_links.txt +1 -0
- parsmeet-1.2.0/ParsMeet.egg-info/requires.txt +2 -0
- parsmeet-1.2.0/ParsMeet.egg-info/top_level.txt +1 -0
- parsmeet-1.2.0/README.md +1 -0
- parsmeet-1.2.0/pyproject.toml +23 -0
- parsmeet-1.2.0/setup.cfg +4 -0
- parsmeet-1.2.0/setup.py +8 -0
parsmeet-1.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ParsMeet
|
|
3
|
+
Version: 1.2.0
|
|
4
|
+
Summary: Python library for interacting with CodeMeet platform
|
|
5
|
+
Author-email: MrLunar-ir <mrlunar.ir@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/MrLunar-ir/ParsMeet
|
|
8
|
+
Project-URL: Repository, https://github.com/MrLunar-ir/ParsMeet
|
|
9
|
+
Requires-Python: >=3.8
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
Requires-Dist: httpx>=0.27.0
|
|
12
|
+
Requires-Dist: websockets>=12.0
|
|
13
|
+
|
|
14
|
+
README.md
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
from .bot import Bot, Markdown
|
|
2
|
+
from .client import Client
|
|
3
|
+
from .models import Room, User
|
|
4
|
+
from .exceptions import ParsMeetError, ParsMeetAuthError
|
|
5
|
+
from .database import Database
|
|
6
|
+
|
|
7
|
+
__all__ = ["Bot", "Markdown", "Client", "Room", "User", "ParsMeetError", "ParsMeetAuthError", "Database"]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import httpx
|
|
2
|
+
import asyncio
|
|
3
|
+
from ..exceptions import ParsMeetAuthError
|
|
4
|
+
|
|
5
|
+
class AuthAPI:
|
|
6
|
+
def __init__(self, client: httpx.AsyncClient):
|
|
7
|
+
self.client = client
|
|
8
|
+
|
|
9
|
+
async def _login(self, username: str, password: str) -> str:
|
|
10
|
+
token = self.client.headers.get('Authorization', '').replace('Bearer ', '')
|
|
11
|
+
url = f"/bot{token}/getMe"
|
|
12
|
+
response = await self.client.get(url)
|
|
13
|
+
if response.status_code == 200:
|
|
14
|
+
return response.json().get("result", {}).get("username", "unknown")
|
|
15
|
+
raise ParsMeetAuthError("Invalid credentials")
|
|
16
|
+
|
|
17
|
+
def login(self, username: str, password: str) -> str:
|
|
18
|
+
return asyncio.run(self._login(username, password))
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import httpx
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from ..models import Room
|
|
4
|
+
|
|
5
|
+
class RoomsAPI:
|
|
6
|
+
def __init__(self, client: httpx.AsyncClient, loop):
|
|
7
|
+
self.client = client
|
|
8
|
+
self.loop = loop
|
|
9
|
+
|
|
10
|
+
def _run(self, coro):
|
|
11
|
+
return self.loop.run_until_complete(coro)
|
|
12
|
+
|
|
13
|
+
async def _create_room(self, name: str) -> Room:
|
|
14
|
+
return Room(id="mock_room_id", name=name, created_at=datetime.now())
|
|
15
|
+
|
|
16
|
+
def create_room(self, name: str) -> Room:
|
|
17
|
+
return self._run(self._create_room(name))
|
|
18
|
+
|
|
19
|
+
async def _send_message(self, chat_id: str, text: str, parse_mode: str = "Markdown", reply_markup: dict = None) -> dict:
|
|
20
|
+
url = f"/bot{self.client.headers.get('Authorization', '').split(' ')[-1]}/sendMessage"
|
|
21
|
+
payload = {"chat_id": chat_id, "text": text, "parse_mode": parse_mode}
|
|
22
|
+
if reply_markup:
|
|
23
|
+
payload["reply_markup"] = reply_markup
|
|
24
|
+
response = await self.client.post(url, json=payload)
|
|
25
|
+
return response.json() if response.status_code == 200 else {"error": response.text}
|
|
26
|
+
|
|
27
|
+
def send_message(self, chat_id: str, text: str, parse_mode: str = "Markdown", reply_markup: dict = None) -> dict:
|
|
28
|
+
return self._run(self._send_message(chat_id, text, parse_mode, reply_markup))
|
|
29
|
+
|
|
30
|
+
async def _edit_message(self, chat_id: str, message_id: int, text: str, parse_mode: str = "Markdown", reply_markup: dict = None) -> dict:
|
|
31
|
+
url = f"/bot{self.client.headers.get('Authorization', '').split(' ')[-1]}/editMessageText"
|
|
32
|
+
payload = {"chat_id": chat_id, "message_id": message_id, "text": text, "parse_mode": parse_mode}
|
|
33
|
+
if reply_markup:
|
|
34
|
+
payload["reply_markup"] = reply_markup
|
|
35
|
+
response = await self.client.post(url, json=payload)
|
|
36
|
+
return response.json() if response.status_code == 200 else {"error": response.text}
|
|
37
|
+
|
|
38
|
+
def edit_message(self, chat_id: str, message_id: int, text: str, parse_mode: str = "Markdown", reply_markup: dict = None) -> dict:
|
|
39
|
+
return self._run(self._edit_message(chat_id, message_id, text, parse_mode, reply_markup))
|
|
40
|
+
|
|
41
|
+
async def _delete_message(self, chat_id: str, message_id: int) -> dict:
|
|
42
|
+
url = f"/bot{self.client.headers.get('Authorization', '').split(' ')[-1]}/deleteMessage"
|
|
43
|
+
payload = {"chat_id": chat_id, "message_id": message_id}
|
|
44
|
+
response = await self.client.post(url, json=payload)
|
|
45
|
+
return response.json() if response.status_code == 200 else {"error": response.text}
|
|
46
|
+
|
|
47
|
+
def delete_message(self, chat_id: str, message_id: int) -> dict:
|
|
48
|
+
return self._run(self._delete_message(chat_id, message_id))
|
|
49
|
+
|
|
50
|
+
async def _send_photo(self, chat_id: str, photo: str, caption: str = "", parse_mode: str = "Markdown", reply_markup: dict = None) -> dict:
|
|
51
|
+
url = f"/bot{self.client.headers.get('Authorization', '').split(' ')[-1]}/sendPhoto"
|
|
52
|
+
payload = {"chat_id": chat_id, "photo": photo, "caption": caption, "parse_mode": parse_mode}
|
|
53
|
+
if reply_markup:
|
|
54
|
+
payload["reply_markup"] = reply_markup
|
|
55
|
+
response = await self.client.post(url, json=payload)
|
|
56
|
+
return response.json() if response.status_code == 200 else {"error": response.text}
|
|
57
|
+
|
|
58
|
+
def send_photo(self, chat_id: str, photo: str, caption: str = "", parse_mode: str = "Markdown", reply_markup: dict = None) -> dict:
|
|
59
|
+
return self._run(self._send_photo(chat_id, photo, caption, parse_mode, reply_markup))
|
|
60
|
+
|
|
61
|
+
async def _send_document(self, chat_id: str, document: str, caption: str = "", parse_mode: str = "Markdown", reply_markup: dict = None) -> dict:
|
|
62
|
+
url = f"/bot{self.client.headers.get('Authorization', '').split(' ')[-1]}/sendDocument"
|
|
63
|
+
payload = {"chat_id": chat_id, "document": document, "caption": caption, "parse_mode": parse_mode}
|
|
64
|
+
if reply_markup:
|
|
65
|
+
payload["reply_markup"] = reply_markup
|
|
66
|
+
response = await self.client.post(url, json=payload)
|
|
67
|
+
return response.json() if response.status_code == 200 else {"error": response.text}
|
|
68
|
+
|
|
69
|
+
def send_document(self, chat_id: str, document: str, caption: str = "", parse_mode: str = "Markdown", reply_markup: dict = None) -> dict:
|
|
70
|
+
return self._run(self._send_document(chat_id, document, caption, parse_mode, reply_markup))
|
|
71
|
+
|
|
72
|
+
async def _set_my_commands(self, commands: list) -> dict:
|
|
73
|
+
url = f"/bot{self.client.headers.get('Authorization', '').split(' ')[-1]}/setMyCommands"
|
|
74
|
+
response = await self.client.post(url, json={"commands": commands})
|
|
75
|
+
return response.json() if response.status_code == 200 else {"error": response.text}
|
|
76
|
+
|
|
77
|
+
def set_my_commands(self, commands: list) -> dict:
|
|
78
|
+
return self._run(self._set_my_commands(commands))
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import httpx
|
|
3
|
+
import threading
|
|
4
|
+
import time
|
|
5
|
+
import re
|
|
6
|
+
from .api.auth import AuthAPI
|
|
7
|
+
from .api.rooms import RoomsAPI
|
|
8
|
+
from .database import Database
|
|
9
|
+
|
|
10
|
+
class Markdown:
|
|
11
|
+
@staticmethod
|
|
12
|
+
def bold(text): return f"**{text}**"
|
|
13
|
+
@staticmethod
|
|
14
|
+
def italic(text): return f"__{text}__"
|
|
15
|
+
@staticmethod
|
|
16
|
+
def code(text): return f"`{text}`"
|
|
17
|
+
@staticmethod
|
|
18
|
+
def spoiler(text): return f"||{text}||"
|
|
19
|
+
@staticmethod
|
|
20
|
+
def link(text, url): return f"[{text}]({url})"
|
|
21
|
+
|
|
22
|
+
YELLOW = "\033[93m"
|
|
23
|
+
RESET = "\033[0m"
|
|
24
|
+
|
|
25
|
+
class Bot:
|
|
26
|
+
def __init__(self, token: str, base_url: str = "https://botapi.codemeet.chat"):
|
|
27
|
+
self.token = token
|
|
28
|
+
self.loop = asyncio.new_event_loop()
|
|
29
|
+
asyncio.set_event_loop(self.loop)
|
|
30
|
+
self._http_client = httpx.AsyncClient(
|
|
31
|
+
base_url=base_url,
|
|
32
|
+
headers={"Authorization": f"Bearer {token}"},
|
|
33
|
+
timeout=None
|
|
34
|
+
)
|
|
35
|
+
self.auth = AuthAPI(self._http_client)
|
|
36
|
+
self.rooms = RoomsAPI(self._http_client, self.loop)
|
|
37
|
+
self._handlers = {}
|
|
38
|
+
self.db = Database()
|
|
39
|
+
self._last_update_id = 0
|
|
40
|
+
self._auto_task = None
|
|
41
|
+
self._stop_auto = False
|
|
42
|
+
self._stop = False
|
|
43
|
+
self._paused = False
|
|
44
|
+
self._input_thread = None
|
|
45
|
+
self._ads_filter_enabled = False
|
|
46
|
+
self._ads_pattern = re.compile(
|
|
47
|
+
r'(https?://|www\.|t\.me/|@\w+|telegram\.me|bit\.ly|tinyurl\.com|'
|
|
48
|
+
r'@[a-zA-Z0-9_]{4,}|[0-9]{5,}|joinchat|/join/|ads?|promo|'
|
|
49
|
+
r'\b(?:ad|sponsor|buy now|click here|offer|discount|free)\b)',
|
|
50
|
+
re.IGNORECASE
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
def _run(self, coro):
|
|
54
|
+
return self.loop.run_until_complete(coro)
|
|
55
|
+
|
|
56
|
+
def _trigger(self, event_name: str, data):
|
|
57
|
+
if event_name in self._handlers:
|
|
58
|
+
for handler in self._handlers[event_name]:
|
|
59
|
+
handler(data)
|
|
60
|
+
|
|
61
|
+
def on_save_message(self, func=None):
|
|
62
|
+
def decorator(actual_func):
|
|
63
|
+
if "save_message" not in self._handlers:
|
|
64
|
+
self._handlers["save_message"] = []
|
|
65
|
+
self._handlers["save_message"].append(actual_func)
|
|
66
|
+
return actual_func
|
|
67
|
+
if func is None:
|
|
68
|
+
return decorator
|
|
69
|
+
return decorator(func)
|
|
70
|
+
|
|
71
|
+
def on_message_group(self, func=None):
|
|
72
|
+
def decorator(actual_func):
|
|
73
|
+
if "message_group" not in self._handlers:
|
|
74
|
+
self._handlers["message_group"] = []
|
|
75
|
+
self._handlers["message_group"].append(actual_func)
|
|
76
|
+
return actual_func
|
|
77
|
+
if func is None:
|
|
78
|
+
return decorator
|
|
79
|
+
return decorator(func)
|
|
80
|
+
|
|
81
|
+
def on_send_message(self, func=None):
|
|
82
|
+
def decorator(actual_func):
|
|
83
|
+
if "send_message" not in self._handlers:
|
|
84
|
+
self._handlers["send_message"] = []
|
|
85
|
+
self._handlers["send_message"].append(actual_func)
|
|
86
|
+
return actual_func
|
|
87
|
+
if func is None:
|
|
88
|
+
return decorator
|
|
89
|
+
return decorator(func)
|
|
90
|
+
|
|
91
|
+
def on_callback_query(self, func=None):
|
|
92
|
+
def decorator(actual_func):
|
|
93
|
+
if "callback_query" not in self._handlers:
|
|
94
|
+
self._handlers["callback_query"] = []
|
|
95
|
+
self._handlers["callback_query"].append(actual_func)
|
|
96
|
+
return actual_func
|
|
97
|
+
if func is None:
|
|
98
|
+
return decorator
|
|
99
|
+
return decorator(func)
|
|
100
|
+
|
|
101
|
+
def send_message(self, chat_id: str, text: str, parse_mode: str = "Markdown", reply_markup: dict = None) -> dict:
|
|
102
|
+
result = self._run(self.rooms._send_message(chat_id, text, parse_mode, reply_markup))
|
|
103
|
+
self._trigger("send_message", {"chat_id": chat_id, "text": text})
|
|
104
|
+
return result
|
|
105
|
+
|
|
106
|
+
def edit_message(self, chat_id: str, message_id: int, text: str, parse_mode: str = "Markdown", reply_markup: dict = None) -> dict:
|
|
107
|
+
return self._run(self.rooms._edit_message(chat_id, message_id, text, parse_mode, reply_markup))
|
|
108
|
+
|
|
109
|
+
def delete_message(self, chat_id: str, message_id: int) -> dict:
|
|
110
|
+
return self._run(self.rooms._delete_message(chat_id, message_id))
|
|
111
|
+
|
|
112
|
+
def send_photo(self, chat_id: str, photo: str, caption: str = "", parse_mode: str = "Markdown", reply_markup: dict = None) -> dict:
|
|
113
|
+
return self._run(self.rooms._send_photo(chat_id, photo, caption, parse_mode, reply_markup))
|
|
114
|
+
|
|
115
|
+
def send_document(self, chat_id: str, document: str, caption: str = "", parse_mode: str = "Markdown", reply_markup: dict = None) -> dict:
|
|
116
|
+
return self._run(self.rooms._send_document(chat_id, document, caption, parse_mode, reply_markup))
|
|
117
|
+
|
|
118
|
+
def set_my_commands(self, commands: list) -> dict:
|
|
119
|
+
return self._run(self.rooms._set_my_commands(commands))
|
|
120
|
+
|
|
121
|
+
def enable_ads_filter(self):
|
|
122
|
+
self._ads_filter_enabled = True
|
|
123
|
+
print("Ads filter enabled.")
|
|
124
|
+
|
|
125
|
+
def disable_ads_filter(self):
|
|
126
|
+
self._ads_filter_enabled = False
|
|
127
|
+
print("Ads filter disabled.")
|
|
128
|
+
|
|
129
|
+
def auto_send(self, interval: int, callback_func):
|
|
130
|
+
def run_auto():
|
|
131
|
+
self._stop_auto = False
|
|
132
|
+
while not self._stop_auto:
|
|
133
|
+
time.sleep(interval)
|
|
134
|
+
if self._stop_auto:
|
|
135
|
+
break
|
|
136
|
+
result = callback_func()
|
|
137
|
+
if result:
|
|
138
|
+
self.send_message(result["chat_id"], result["text"], result.get("parse_mode", "Markdown"))
|
|
139
|
+
self._auto_task = threading.Thread(target=run_auto, daemon=True)
|
|
140
|
+
self._auto_task.start()
|
|
141
|
+
|
|
142
|
+
def stop_auto_send(self):
|
|
143
|
+
self._stop_auto = True
|
|
144
|
+
|
|
145
|
+
def _listen_input(self):
|
|
146
|
+
while not self._stop:
|
|
147
|
+
try:
|
|
148
|
+
cmd = input(f"{YELLOW}bot Console >>>{RESET} ").strip().lower()
|
|
149
|
+
if cmd == "bot.off()":
|
|
150
|
+
print("Shutting down bot...")
|
|
151
|
+
self.off()
|
|
152
|
+
break
|
|
153
|
+
elif cmd == "bot.pause()":
|
|
154
|
+
self._paused = True
|
|
155
|
+
print("Bot paused.")
|
|
156
|
+
elif cmd == "bot.on()":
|
|
157
|
+
if self._paused:
|
|
158
|
+
self._paused = False
|
|
159
|
+
print("Bot resumed.")
|
|
160
|
+
else:
|
|
161
|
+
print("Bot is already running!")
|
|
162
|
+
elif cmd == "bot.filter.on()":
|
|
163
|
+
self.enable_ads_filter()
|
|
164
|
+
elif cmd == "bot.filter.off()":
|
|
165
|
+
self.disable_ads_filter()
|
|
166
|
+
else:
|
|
167
|
+
print("Invalid command. Valid: bot.off(), bot.pause(), bot.on(), bot.filter.on(), bot.filter.off()")
|
|
168
|
+
except EOFError:
|
|
169
|
+
break
|
|
170
|
+
|
|
171
|
+
def off(self):
|
|
172
|
+
self._stop = True
|
|
173
|
+
self.stop_auto_send()
|
|
174
|
+
self._paused = False
|
|
175
|
+
|
|
176
|
+
def _check_ads(self, text):
|
|
177
|
+
if not self._ads_filter_enabled:
|
|
178
|
+
return False
|
|
179
|
+
return bool(self._ads_pattern.search(text))
|
|
180
|
+
|
|
181
|
+
async def _get_updates(self, timeout: int = 5) -> list:
|
|
182
|
+
if self._paused:
|
|
183
|
+
await asyncio.sleep(1)
|
|
184
|
+
return []
|
|
185
|
+
url = f"/bot{self.token}/getUpdates"
|
|
186
|
+
params = {"timeout": timeout, "offset": self._last_update_id + 1}
|
|
187
|
+
try:
|
|
188
|
+
response = await self._http_client.get(url, params=params)
|
|
189
|
+
if response.status_code == 200:
|
|
190
|
+
return response.json().get("result", [])
|
|
191
|
+
except Exception:
|
|
192
|
+
return []
|
|
193
|
+
return []
|
|
194
|
+
|
|
195
|
+
def run(self, timeout: int = 5):
|
|
196
|
+
print(f"Bot {self.get_me()} is running...")
|
|
197
|
+
print("Type 'bot.off()' to stop, 'bot.pause()' to pause, 'bot.on()' to resume.")
|
|
198
|
+
print("Type 'bot.filter.on()' to enable ads filter, 'bot.filter.off()' to disable.")
|
|
199
|
+
self._input_thread = threading.Thread(target=self._listen_input, daemon=True)
|
|
200
|
+
self._input_thread.start()
|
|
201
|
+
while not self._stop:
|
|
202
|
+
updates = self._run(self._get_updates(timeout))
|
|
203
|
+
for update in updates:
|
|
204
|
+
if "message" in update:
|
|
205
|
+
msg = update["message"]
|
|
206
|
+
if msg.get("from", {}).get("is_bot", False) or msg.get("from", {}).get("username") == self.get_me():
|
|
207
|
+
self._last_update_id = update["update_id"]
|
|
208
|
+
continue
|
|
209
|
+
self._last_update_id = update["update_id"]
|
|
210
|
+
chat_id = msg["chat"]["id"]
|
|
211
|
+
text = msg.get("text", "")
|
|
212
|
+
username = msg.get("from", {}).get("username", "Unknown")
|
|
213
|
+
|
|
214
|
+
# فیلتر تبلیغات
|
|
215
|
+
if self._check_ads(text):
|
|
216
|
+
try:
|
|
217
|
+
self.delete_message(chat_id, msg["message_id"])
|
|
218
|
+
print(f"Deleted ad message from {username}: {text}")
|
|
219
|
+
except Exception:
|
|
220
|
+
pass
|
|
221
|
+
|
|
222
|
+
self.db.save_message(chat_id, text)
|
|
223
|
+
self._trigger("save_message", {"chat_id": chat_id, "text": text, "username": username})
|
|
224
|
+
self._trigger("message_group", {"chat_id": chat_id, "text": text, "username": username})
|
|
225
|
+
elif "callback_query" in update:
|
|
226
|
+
cb = update["callback_query"]
|
|
227
|
+
self._last_update_id = update["update_id"]
|
|
228
|
+
chat_id = cb["message"]["chat"]["id"]
|
|
229
|
+
message_id = cb["message"]["message_id"]
|
|
230
|
+
callback_data = cb.get("data", "")
|
|
231
|
+
username = cb.get("from", {}).get("username", "Unknown")
|
|
232
|
+
self._trigger("callback_query", {"chat_id": chat_id, "message_id": message_id, "data": callback_data, "username": username})
|
|
233
|
+
self.close()
|
|
234
|
+
print("Bot stopped.")
|
|
235
|
+
|
|
236
|
+
def get_me(self):
|
|
237
|
+
return self._run(self.auth._login("", ""))
|
|
238
|
+
|
|
239
|
+
def close(self):
|
|
240
|
+
self.stop_auto_send()
|
|
241
|
+
self.db.close()
|
|
242
|
+
self._run(self._http_client.aclose())
|
|
243
|
+
self.loop.close()
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import httpx
|
|
2
|
+
import asyncio
|
|
3
|
+
from .api.auth import AuthAPI
|
|
4
|
+
from .api.rooms import RoomsAPI
|
|
5
|
+
|
|
6
|
+
class Client:
|
|
7
|
+
def __init__(self, api_key: str, base_url: str = "https://botapi.codemeet.chat"):
|
|
8
|
+
self.api_key = api_key
|
|
9
|
+
self.base_url = base_url
|
|
10
|
+
self._http_client = httpx.AsyncClient(
|
|
11
|
+
base_url=base_url,
|
|
12
|
+
headers={"Authorization": f"Bearer {api_key}"}
|
|
13
|
+
)
|
|
14
|
+
self.auth = AuthAPI(self._http_client)
|
|
15
|
+
self.rooms = RoomsAPI(self._http_client)
|
|
16
|
+
|
|
17
|
+
def _run(self, coro):
|
|
18
|
+
return asyncio.run(coro)
|
|
19
|
+
|
|
20
|
+
def close(self):
|
|
21
|
+
return self._run(self._http_client.aclose())
|
|
22
|
+
|
|
23
|
+
def __enter__(self):
|
|
24
|
+
return self
|
|
25
|
+
|
|
26
|
+
def __exit__(self, *args):
|
|
27
|
+
self.close()
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import sqlite3
|
|
2
|
+
import os
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
|
|
5
|
+
class Database:
|
|
6
|
+
def __init__(self, db_name="ParsMeetSaveMessage.db"):
|
|
7
|
+
self.db_name = db_name
|
|
8
|
+
self.conn = sqlite3.connect(db_name)
|
|
9
|
+
self.cursor = self.conn.cursor()
|
|
10
|
+
self._create_table()
|
|
11
|
+
|
|
12
|
+
def _create_table(self):
|
|
13
|
+
self.cursor.execute("""
|
|
14
|
+
CREATE TABLE IF NOT EXISTS messages (
|
|
15
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
16
|
+
chat_id TEXT,
|
|
17
|
+
text TEXT,
|
|
18
|
+
timestamp TEXT
|
|
19
|
+
)
|
|
20
|
+
""")
|
|
21
|
+
self.conn.commit()
|
|
22
|
+
|
|
23
|
+
def save_message(self, chat_id, text):
|
|
24
|
+
timestamp = datetime.now().isoformat()
|
|
25
|
+
self.cursor.execute("INSERT INTO messages (chat_id, text, timestamp) VALUES (?, ?, ?)", (chat_id, text, timestamp))
|
|
26
|
+
self.conn.commit()
|
|
27
|
+
|
|
28
|
+
def get_all_messages(self):
|
|
29
|
+
self.cursor.execute("SELECT * FROM messages ORDER BY id DESC")
|
|
30
|
+
return self.cursor.fetchall()
|
|
31
|
+
|
|
32
|
+
def close(self):
|
|
33
|
+
self.conn.close()
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from typing import List, Optional
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
|
|
5
|
+
@dataclass
|
|
6
|
+
class User:
|
|
7
|
+
id: str
|
|
8
|
+
username: str
|
|
9
|
+
|
|
10
|
+
@dataclass
|
|
11
|
+
class Room:
|
|
12
|
+
id: str
|
|
13
|
+
name: str
|
|
14
|
+
created_at: datetime
|
|
15
|
+
participants: List[User] = field(default_factory=list)
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class CodePayload:
|
|
19
|
+
language: str
|
|
20
|
+
code: str
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .socket import RealtimeClient
|
|
File without changes
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import websockets
|
|
2
|
+
|
|
3
|
+
class RealtimeClient:
|
|
4
|
+
def __init__(self, room_id: str, token: str):
|
|
5
|
+
self.room_id = room_id
|
|
6
|
+
self.token = token
|
|
7
|
+
|
|
8
|
+
async def connect(self):
|
|
9
|
+
uri = f"wss://botapi.codemeet.chat/ws/{self.room_id}?token={self.token}"
|
|
10
|
+
async with websockets.connect(uri) as websocket:
|
|
11
|
+
print(f"Connected to room {self.room_id}")
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ParsMeet
|
|
3
|
+
Version: 1.2.0
|
|
4
|
+
Summary: Python library for interacting with CodeMeet platform
|
|
5
|
+
Author-email: MrLunar-ir <mrlunar.ir@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/MrLunar-ir/ParsMeet
|
|
8
|
+
Project-URL: Repository, https://github.com/MrLunar-ir/ParsMeet
|
|
9
|
+
Requires-Python: >=3.8
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
Requires-Dist: httpx>=0.27.0
|
|
12
|
+
Requires-Dist: websockets>=12.0
|
|
13
|
+
|
|
14
|
+
README.md
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
setup.py
|
|
4
|
+
ParsMeet/__init__.py
|
|
5
|
+
ParsMeet/bot.py
|
|
6
|
+
ParsMeet/client.py
|
|
7
|
+
ParsMeet/database.py
|
|
8
|
+
ParsMeet/exceptions.py
|
|
9
|
+
ParsMeet/models.py
|
|
10
|
+
ParsMeet.egg-info/PKG-INFO
|
|
11
|
+
ParsMeet.egg-info/SOURCES.txt
|
|
12
|
+
ParsMeet.egg-info/dependency_links.txt
|
|
13
|
+
ParsMeet.egg-info/requires.txt
|
|
14
|
+
ParsMeet.egg-info/top_level.txt
|
|
15
|
+
ParsMeet/api/__init__.py
|
|
16
|
+
ParsMeet/api/auth.py
|
|
17
|
+
ParsMeet/api/rooms.py
|
|
18
|
+
ParsMeet/realtime/__init__.py
|
|
19
|
+
ParsMeet/realtime/py.typed
|
|
20
|
+
ParsMeet/realtime/socket.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ParsMeet
|
parsmeet-1.2.0/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
README.md
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "ParsMeet"
|
|
7
|
+
version = "1.2.0"
|
|
8
|
+
description = "Python library for interacting with CodeMeet platform"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [{ name = "MrLunar-ir", email = "mrlunar.ir@gmail.com" }]
|
|
13
|
+
dependencies = [
|
|
14
|
+
"httpx>=0.27.0",
|
|
15
|
+
"websockets>=12.0",
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
[project.urls]
|
|
19
|
+
Homepage = "https://github.com/MrLunar-ir/ParsMeet"
|
|
20
|
+
Repository = "https://github.com/MrLunar-ir/ParsMeet"
|
|
21
|
+
|
|
22
|
+
[tool.setuptools.packages.find]
|
|
23
|
+
include = ["ParsMeet*"]
|
parsmeet-1.2.0/setup.cfg
ADDED