Skip to content

Commit dc2498c

Browse files
committed
Merge branch 'master' into dev-1.2.0
2 parents 1f70b1c + 3398e04 commit dc2498c

5 files changed

Lines changed: 233 additions & 20 deletions

File tree

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
from trueconf import Bot
2+
from trueconf.exceptions import ApiErrorException
3+
import asyncio
4+
import logging
5+
from pathlib import Path
6+
7+
SERVER_ADDR = ""
8+
BOT_USERNAME = ""
9+
BOT_PASSWORD = ""
10+
PATH_LIST_USERS = ""
11+
CHANNEL_NAME = ""
12+
13+
bot = Bot.from_credentials(
14+
server=SERVER_ADDR,
15+
username=BOT_USERNAME,
16+
password=BOT_PASSWORD,
17+
verify_ssl=False,
18+
)
19+
20+
Path("logs").mkdir(parents=True, exist_ok=True)
21+
22+
logging.basicConfig(
23+
level=logging.DEBUG,
24+
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
25+
filename="logs/bot.log",
26+
encoding="utf-8",
27+
)
28+
29+
async def main():
30+
await bot.start()
31+
await bot.connected_event.wait()
32+
await bot.authorized_event.wait()
33+
34+
resp = await bot.create_channel(title=CHANNEL_NAME)
35+
with open(PATH_LIST_USERS, "r") as file:
36+
for line in file:
37+
user_id = line.strip()
38+
if user_id:
39+
try:
40+
await bot.add_participant_to_chat(resp.chat_id, user_id=user_id, display_history=True)
41+
except ApiErrorException as e:
42+
if e.code == 309:
43+
continue
44+
45+
await bot.shutdown()
46+
47+
if __name__ == "__main__":
48+
asyncio.run(main())

trueconf/client/bot.py

Lines changed: 58 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import ssl
88
import tempfile
99
import websockets
10+
import warnings
1011
from pathlib import Path
1112
from aiohttp import ClientSession, ClientTimeout, TCPConnector, FormData
1213
from async_property import async_cached_property
@@ -348,7 +349,7 @@ async def __upload_file_to_server(
348349
preview: InputFile | None = None,
349350
is_sticker: bool = False,
350351
timeout: int = 60,
351-
) -> str | None:
352+
) -> str:
352353
"""
353354
Uploads a file to the server and returns a temporary file identifier (temporalFileId).
354355
@@ -401,7 +402,7 @@ async def __upload_file_to_server(
401402

402403
try:
403404
async with ClientSession(connector=connector, timeout=timeout) as session:
404-
data = FormData()
405+
data = FormData(quote_fields=False)
405406
data.add_field(
406407
name="file",
407408
value= await file.read(),
@@ -436,7 +437,13 @@ async def _send_ws_payload(self, message: dict) -> bool:
436437
return False
437438

438439
async def __connect_and_listen(self):
439-
ssl_context = ssl._create_unverified_context() if self.https else None
440+
ssl_context = None
441+
if self.https:
442+
if self.verify_ssl:
443+
ssl_context = ssl.create_default_context()
444+
else:
445+
ssl_context = ssl._create_unverified_context()
446+
440447
uri = f"wss://{self.server}:{self.web_port}/websocket/chat_bot" if self.https else f"ws://{self.server}:{self.web_port}/websocket/chat_bot"
441448

442449
while not self._stop:
@@ -1172,6 +1179,12 @@ async def reply_message(
11721179
SendMessageResponse: Object containing the result of the message delivery.
11731180
"""
11741181

1182+
warnings.warn(
1183+
"reply_message is deprecated, use send_message(..., reply_message_id=...) instead",
1184+
DeprecationWarning,
1185+
stacklevel=2
1186+
)
1187+
11751188
call = SendMessage(
11761189
chat_id=chat_id,
11771190
reply_message_id=message_id,
@@ -1216,6 +1229,7 @@ async def send_document(
12161229
file: InputFile,
12171230
caption: str | None = None,
12181231
parse_mode: ParseMode | str = ParseMode.TEXT,
1232+
reply_message_id: str | None = None
12191233
) -> SendFileResponse:
12201234
"""
12211235
Sends a document or any arbitrary file to the specified chat.
@@ -1232,6 +1246,7 @@ async def send_document(
12321246
file (InputFile): The file to be uploaded. Must be a subclass of `InputFile`.
12331247
caption (str | None): Optional caption text to be sent with the file.
12341248
parse_mode (ParseMode | str): Text formatting mode (e.g., Markdown, HTML, or plain text).
1249+
reply_message_id (str, optional): Optional identifier of the message to which this message is a reply.
12351250
12361251
Returns:
12371252
SendFileResponse: An object containing the result of the file upload.
@@ -1253,14 +1268,21 @@ async def send_document(
12531268
file=file,
12541269
)
12551270

1256-
call = SendFile(chat_id=chat_id, temporal_file_id=temporal_file_id, text=caption, parse_mode=parse_mode)
1271+
call = SendFile(
1272+
chat_id=chat_id,
1273+
temporal_file_id=temporal_file_id,
1274+
text=caption,
1275+
parse_mode=parse_mode,
1276+
reply_message_id=reply_message_id
1277+
)
12571278
return await self(call)
12581279

12591280
async def send_message(
12601281
self,
12611282
chat_id: str,
12621283
text: str,
1263-
parse_mode: ParseMode | str = ParseMode.TEXT
1284+
parse_mode: ParseMode | str = ParseMode.TEXT,
1285+
reply_message_id: str | None = None
12641286
) -> SendMessageResponse:
12651287
"""
12661288
Sends a message to the specified chat.
@@ -1273,13 +1295,19 @@ async def send_message(
12731295
text (str): Text content of the message.
12741296
parse_mode (ParseMode | str, optional): Text formatting mode.
12751297
Defaults to plain text.
1298+
reply_message_id (str, optional): Optional identifier of the message to which this message is a reply.
12761299
12771300
Returns:
12781301
SendMessageResponse: Object containing the result of the message delivery.
12791302
"""
12801303

12811304
loggers.chatbot.info(f"✉️ Sending message to {chat_id}")
1282-
call = SendMessage(chat_id=chat_id, text=text, parse_mode=parse_mode)
1305+
call = SendMessage(
1306+
chat_id=chat_id,
1307+
text=text,
1308+
parse_mode=parse_mode,
1309+
reply_message_id=reply_message_id
1310+
)
12831311
return await self(call)
12841312

12851313
async def send_photo(
@@ -1288,7 +1316,8 @@ async def send_photo(
12881316
file: InputFile,
12891317
preview: InputFile | None,
12901318
caption: str | None = None,
1291-
parse_mode: ParseMode | str = ParseMode.TEXT
1319+
parse_mode: ParseMode | str = ParseMode.TEXT,
1320+
reply_message_id: str | None = None
12921321
) -> SendFileResponse:
12931322
"""
12941323
Sends a photo to the specified chat, with optional caption and preview support.
@@ -1311,6 +1340,7 @@ async def send_photo(
13111340
preview (InputFile | None): Optional preview image. Must also be an `InputFile` if provided.
13121341
caption (str | None): Optional caption to be sent along with the image.
13131342
parse_mode (ParseMode | str): Formatting mode for the caption (e.g., Markdown, HTML, plain text).
1343+
reply_message_id (str, optional): Optional identifier of the message to which this message is a reply.
13141344
13151345
Returns:
13161346
SendFileResponse: An object containing the result of the file upload.
@@ -1334,10 +1364,21 @@ async def send_photo(
13341364
preview=preview,
13351365
)
13361366

1337-
call = SendFile(chat_id=chat_id, temporal_file_id=temporal_file_id, text=caption, parse_mode=parse_mode)
1367+
call = SendFile(
1368+
chat_id=chat_id,
1369+
temporal_file_id=temporal_file_id,
1370+
text=caption,
1371+
parse_mode=parse_mode,
1372+
reply_message_id=reply_message_id
1373+
)
13381374
return await self(call)
13391375

1340-
async def send_sticker(self, chat_id: str, file: InputFile) -> SendFileResponse:
1376+
async def send_sticker(
1377+
self,
1378+
chat_id: str,
1379+
file: InputFile,
1380+
reply_message_id: str | None = None
1381+
) -> SendFileResponse:
13411382
"""
13421383
Sends a sticker in WebP format to the specified chat.
13431384
@@ -1354,6 +1395,7 @@ async def send_sticker(self, chat_id: str, file: InputFile) -> SendFileResponse:
13541395
Args:
13551396
chat_id (str): Identifier of the chat to which the sticker will be sent.
13561397
file (InputFile): The sticker file in WebP format. Must be a subclass of `InputFile`.
1398+
reply_message_id (str, optional): Optional identifier of the message to which this message is a reply.
13571399
13581400
Returns:
13591401
SendFileResponse: An object containing the result of the file upload.
@@ -1378,15 +1420,19 @@ async def send_sticker(self, chat_id: str, file: InputFile) -> SendFileResponse:
13781420
is_sticker=True
13791421
)
13801422

1381-
call = SendFile(chat_id=chat_id, temporal_file_id=temporal_file_id)
1423+
call = SendFile(
1424+
chat_id=chat_id,
1425+
temporal_file_id=temporal_file_id,
1426+
reply_message_id=reply_message_id
1427+
)
13821428
return await self(call)
13831429

13841430
async def send_survey(
13851431
self,
13861432
chat_id: str,
13871433
title: str,
13881434
survey_campaign_id: str,
1389-
reply_message_id: str = None,
1435+
reply_message_id: str | None = None,
13901436
survey_type: SurveyType = SurveyType.NON_ANONYMOUS,
13911437
) -> SendSurveyResponse:
13921438
"""
@@ -1399,7 +1445,7 @@ async def send_survey(
13991445
chat_id (str): Identifier of the chat to send the survey to.
14001446
title (str): Title of the survey displayed in the chat.
14011447
survey_campaign_id (str): Identifier of the survey campaign.
1402-
reply_message_id (str, optional): Identifier of the message being replied to.
1448+
reply_message_id (str, optional): Optional identifier of the message to which this message is a reply.
14031449
survey_type (SurveyType, optional): Type of the survey (anonymous or non-anonymous). Defaults to non-anonymous.
14041450
14051451
Returns:

trueconf/methods/send_file.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,17 @@ class SendFile(TrueConfMethod[SendFileResponse]):
1212

1313
chat_id: str
1414
temporal_file_id: str
15-
text: str = None
16-
parse_mode: str = None
15+
text: str | None = None
16+
parse_mode: str | None = None
17+
reply_message_id: str | None = None
1718

1819
def __post_init__(self):
1920
super().__init__()
2021

2122
def payload(self):
2223
return {
2324
"chatId": self.chat_id,
25+
"replyMessageId": self.reply_message_id,
2426
"content": {
2527
"temporalFileId": self.temporal_file_id,
2628
"caption":{

0 commit comments

Comments
 (0)