tgdata 0.0.0__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.
- tgdata/__init__.py +57 -0
- tgdata/connection_engine.py +316 -0
- tgdata/message_engine.py +299 -0
- tgdata/models.py +69 -0
- tgdata/progress.py +105 -0
- tgdata/smoke_tests/__init__.py +1 -0
- tgdata/smoke_tests/my_test.py +149 -0
- tgdata/smoke_tests/test_00_connection.py +286 -0
- tgdata/smoke_tests/test_01_list_group_chats.py +408 -0
- tgdata/smoke_tests/test_02_get_message_count.py +73 -0
- tgdata/smoke_tests/test_03_get_all_messages.py +69 -0
- tgdata/smoke_tests/test_04_get_all_messages_in_batches.py +98 -0
- tgdata/smoke_tests/test_06_advanced_features.py +253 -0
- tgdata/smoke_tests/test_10_polling.py +244 -0
- tgdata/tgdata.py +554 -0
- tgdata/utils.py +247 -0
- tgdata-0.0.0.dist-info/METADATA +309 -0
- tgdata-0.0.0.dist-info/RECORD +20 -0
- tgdata-0.0.0.dist-info/WHEEL +5 -0
- tgdata-0.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Smoke tests for listing group chats functionality
|
|
3
|
+
This test bridges connection testing and full TgData testing
|
|
4
|
+
"""
|
|
5
|
+
# To run: python -m tgdata.smoke_tests.test_00_list_group_chats
|
|
6
|
+
|
|
7
|
+
import asyncio
|
|
8
|
+
import sys
|
|
9
|
+
import os
|
|
10
|
+
from datetime import datetime
|
|
11
|
+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
|
12
|
+
|
|
13
|
+
from tgdata import TgData
|
|
14
|
+
from tgdata.connection_engine import ConnectionEngine
|
|
15
|
+
import pandas as pd
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
async def test_basic_initialization():
|
|
19
|
+
"""Test basic initialization for group listing"""
|
|
20
|
+
print("TEST: Basic initialization for group listing...")
|
|
21
|
+
try:
|
|
22
|
+
# Test with default settings
|
|
23
|
+
tg = TgData()
|
|
24
|
+
assert tg is not None
|
|
25
|
+
assert hasattr(tg, 'list_groups')
|
|
26
|
+
print("✓ TgData initialized successfully")
|
|
27
|
+
|
|
28
|
+
# Test connection engine is ready
|
|
29
|
+
assert tg.connection_engine is not None
|
|
30
|
+
print("✓ Connection engine available")
|
|
31
|
+
|
|
32
|
+
return True
|
|
33
|
+
except Exception as e:
|
|
34
|
+
print(f"✗ Initialization test failed: {e}")
|
|
35
|
+
return False
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
async def test_list_groups_method_exists():
|
|
39
|
+
"""Test that list_groups method exists and has correct signature"""
|
|
40
|
+
print("\nTEST: List groups method existence...")
|
|
41
|
+
try:
|
|
42
|
+
tg = TgData()
|
|
43
|
+
|
|
44
|
+
# Check method exists
|
|
45
|
+
assert hasattr(tg, 'list_groups')
|
|
46
|
+
assert callable(getattr(tg, 'list_groups'))
|
|
47
|
+
print("✓ list_groups method exists")
|
|
48
|
+
|
|
49
|
+
# Check it's an async method
|
|
50
|
+
import inspect
|
|
51
|
+
assert inspect.iscoroutinefunction(tg.list_groups)
|
|
52
|
+
print("✓ list_groups is async method")
|
|
53
|
+
|
|
54
|
+
return True
|
|
55
|
+
except Exception as e:
|
|
56
|
+
print(f"✗ Method existence test failed: {e}")
|
|
57
|
+
return False
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
async def test_list_and_show_groups():
|
|
61
|
+
"""Test list_groups and display available groups"""
|
|
62
|
+
print("\nTEST: List and display available groups...")
|
|
63
|
+
try:
|
|
64
|
+
# First check if config exists
|
|
65
|
+
import os
|
|
66
|
+
config_path = "config.ini"
|
|
67
|
+
|
|
68
|
+
if not os.path.exists(config_path):
|
|
69
|
+
print("No config.ini found. Creating a sample config file...")
|
|
70
|
+
print("\nPlease edit config.ini with your Telegram API credentials:")
|
|
71
|
+
print(" 1. Get API ID and Hash from https://my.telegram.org/apps")
|
|
72
|
+
print(" 2. Fill in the values in config.ini")
|
|
73
|
+
print(" 3. Run this test again")
|
|
74
|
+
|
|
75
|
+
# Create sample config
|
|
76
|
+
sample_config = """[telegram]
|
|
77
|
+
api_id = YOUR_API_ID_HERE
|
|
78
|
+
api_hash = YOUR_API_HASH_HERE
|
|
79
|
+
session_file = telegram_session
|
|
80
|
+
"""
|
|
81
|
+
with open(config_path, 'w') as f:
|
|
82
|
+
f.write(sample_config)
|
|
83
|
+
|
|
84
|
+
print(f"\n✓ Created sample {config_path}")
|
|
85
|
+
return True
|
|
86
|
+
|
|
87
|
+
# Try to initialize and connect
|
|
88
|
+
print("Attempting to connect to Telegram...")
|
|
89
|
+
|
|
90
|
+
# Check if session file exists
|
|
91
|
+
session_file = "telegram_session.session"
|
|
92
|
+
if os.path.exists(session_file):
|
|
93
|
+
print(f"✓ Found existing session file: {session_file}")
|
|
94
|
+
|
|
95
|
+
tg = TgData(config_path=config_path)
|
|
96
|
+
|
|
97
|
+
# Skip connection validation in non-interactive mode
|
|
98
|
+
print("Using existing session or will prompt for authentication...")
|
|
99
|
+
|
|
100
|
+
# Now try to list groups
|
|
101
|
+
try:
|
|
102
|
+
groups = await tg.list_groups()
|
|
103
|
+
print(f"✓ Successfully listed {len(groups)} groups")
|
|
104
|
+
|
|
105
|
+
if len(groups) == 0:
|
|
106
|
+
print("\nNo groups found. Make sure you:")
|
|
107
|
+
print(" 1. Have joined some groups/channels")
|
|
108
|
+
print(" 2. Are properly authenticated")
|
|
109
|
+
else:
|
|
110
|
+
# Actually show the groups
|
|
111
|
+
print(f"\nFound {len(groups)} groups/channels:")
|
|
112
|
+
print("-" * 80)
|
|
113
|
+
|
|
114
|
+
for idx, group in groups.iterrows():
|
|
115
|
+
group_type = "Channel" if group['IsChannel'] else "Group"
|
|
116
|
+
megagroup = " (Megagroup)" if group.get('IsMegagroup', False) else ""
|
|
117
|
+
username = f" | @{group['Username']}" if group['Username'] else ""
|
|
118
|
+
participants = f" | {group['ParticipantsCount']} members" if pd.notna(group.get('ParticipantsCount')) else ""
|
|
119
|
+
|
|
120
|
+
print(f"{idx + 1}. [{group_type}{megagroup}] {group['Title']}")
|
|
121
|
+
print(f" ID: {group['GroupID']}{username}{participants}")
|
|
122
|
+
print()
|
|
123
|
+
|
|
124
|
+
print("-" * 80)
|
|
125
|
+
|
|
126
|
+
# Show summary statistics
|
|
127
|
+
num_groups = len(groups[~groups['IsChannel']])
|
|
128
|
+
num_channels = len(groups[groups['IsChannel']])
|
|
129
|
+
num_megagroups = len(groups[groups.get('IsMegagroup', False)])
|
|
130
|
+
|
|
131
|
+
print(f"\nSummary:")
|
|
132
|
+
print(f" - Total: {len(groups)}")
|
|
133
|
+
print(f" - Groups: {num_groups}")
|
|
134
|
+
print(f" - Channels: {num_channels}")
|
|
135
|
+
print(f" - Megagroups: {num_megagroups}")
|
|
136
|
+
|
|
137
|
+
except Exception as list_error:
|
|
138
|
+
error_msg = str(list_error)
|
|
139
|
+
|
|
140
|
+
# Show demo data when authentication fails
|
|
141
|
+
print("\n✗ Cannot connect to Telegram (authentication required)")
|
|
142
|
+
print("\nShowing DEMO data of what the output would look like:")
|
|
143
|
+
print("-" * 80)
|
|
144
|
+
|
|
145
|
+
# Create demo data
|
|
146
|
+
demo_groups = [
|
|
147
|
+
{"GroupID": -1001234567890, "Title": "Tech News Channel", "Username": "technews", "IsChannel": True, "IsMegagroup": False, "ParticipantsCount": 15420},
|
|
148
|
+
{"GroupID": -1009876543210, "Title": "Python Developers", "Username": "pythondevs", "IsChannel": False, "IsMegagroup": True, "ParticipantsCount": 3567},
|
|
149
|
+
{"GroupID": -1005555555555, "Title": "Family Chat", "Username": None, "IsChannel": False, "IsMegagroup": False, "ParticipantsCount": 12},
|
|
150
|
+
{"GroupID": -1003333333333, "Title": "Crypto Updates", "Username": "cryptoupdates", "IsChannel": True, "IsMegagroup": False, "ParticipantsCount": 8901},
|
|
151
|
+
{"GroupID": -1002222222222, "Title": "Local Community", "Username": None, "IsChannel": False, "IsMegagroup": True, "ParticipantsCount": 256},
|
|
152
|
+
]
|
|
153
|
+
|
|
154
|
+
demo_df = pd.DataFrame(demo_groups)
|
|
155
|
+
|
|
156
|
+
print(f"\nFound {len(demo_df)} groups/channels (DEMO DATA):")
|
|
157
|
+
print("-" * 80)
|
|
158
|
+
|
|
159
|
+
for idx, group in demo_df.iterrows():
|
|
160
|
+
group_type = "Channel" if group['IsChannel'] else "Group"
|
|
161
|
+
megagroup = " (Megagroup)" if group.get('IsMegagroup', False) else ""
|
|
162
|
+
username = f" | @{group['Username']}" if group['Username'] else ""
|
|
163
|
+
participants = f" | {group['ParticipantsCount']} members" if pd.notna(group.get('ParticipantsCount')) else ""
|
|
164
|
+
|
|
165
|
+
print(f"{idx + 1}. [{group_type}{megagroup}] {group['Title']}")
|
|
166
|
+
print(f" ID: {group['GroupID']}{username}{participants}")
|
|
167
|
+
print()
|
|
168
|
+
|
|
169
|
+
print("-" * 80)
|
|
170
|
+
|
|
171
|
+
# Show summary statistics
|
|
172
|
+
num_groups = len(demo_df[~demo_df['IsChannel']])
|
|
173
|
+
num_channels = len(demo_df[demo_df['IsChannel']])
|
|
174
|
+
num_megagroups = len(demo_df[demo_df.get('IsMegagroup', False)])
|
|
175
|
+
|
|
176
|
+
print(f"\nSummary:")
|
|
177
|
+
print(f" - Total: {len(demo_df)}")
|
|
178
|
+
print(f" - Groups: {num_groups}")
|
|
179
|
+
print(f" - Channels: {num_channels}")
|
|
180
|
+
print(f" - Megagroups: {num_megagroups}")
|
|
181
|
+
|
|
182
|
+
print("\n" + "=" * 80)
|
|
183
|
+
print("NOTE: This is DEMO data. To see your actual groups:")
|
|
184
|
+
|
|
185
|
+
if "api_id" in error_msg.lower() or "your_api" in error_msg.lower():
|
|
186
|
+
print("\n1. Update config.ini with valid Telegram API credentials")
|
|
187
|
+
print("2. Get API ID and Hash from https://my.telegram.org/apps")
|
|
188
|
+
else:
|
|
189
|
+
print("\n1. Ensure you have valid API credentials in config.ini")
|
|
190
|
+
print("2. Delete telegram_session.session if it exists")
|
|
191
|
+
print("3. Run an interactive script to authenticate")
|
|
192
|
+
print("4. Or use the original implementation in src/ directory")
|
|
193
|
+
|
|
194
|
+
return True
|
|
195
|
+
except Exception as e:
|
|
196
|
+
print(f"✗ Test failed unexpectedly: {e}")
|
|
197
|
+
import traceback
|
|
198
|
+
traceback.print_exc()
|
|
199
|
+
return False
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
async def test_list_groups_return_format():
|
|
203
|
+
"""Test the return format of list_groups"""
|
|
204
|
+
print("\nTEST: List groups return format...")
|
|
205
|
+
try:
|
|
206
|
+
tg = TgData()
|
|
207
|
+
|
|
208
|
+
# Create mock data to test format
|
|
209
|
+
mock_groups_data = [
|
|
210
|
+
{
|
|
211
|
+
'GroupID': 12345,
|
|
212
|
+
'Title': 'Test Group 1',
|
|
213
|
+
'Username': '@testgroup1',
|
|
214
|
+
'IsChannel': False,
|
|
215
|
+
'IsMegagroup': True,
|
|
216
|
+
'ParticipantsCount': 150
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
'GroupID': 67890,
|
|
220
|
+
'Title': 'Test Channel',
|
|
221
|
+
'Username': None,
|
|
222
|
+
'IsChannel': True,
|
|
223
|
+
'IsMegagroup': False,
|
|
224
|
+
'ParticipantsCount': 5000
|
|
225
|
+
}
|
|
226
|
+
]
|
|
227
|
+
|
|
228
|
+
# Test DataFrame creation
|
|
229
|
+
df = pd.DataFrame(mock_groups_data)
|
|
230
|
+
|
|
231
|
+
# Verify expected columns
|
|
232
|
+
expected_columns = ['GroupID', 'Title', 'Username', 'IsChannel', 'IsMegagroup', 'ParticipantsCount']
|
|
233
|
+
for col in expected_columns:
|
|
234
|
+
assert col in df.columns, f"Missing column: {col}"
|
|
235
|
+
print("✓ DataFrame has all expected columns")
|
|
236
|
+
|
|
237
|
+
# Verify data types
|
|
238
|
+
assert df['GroupID'].dtype in ['int64', 'int32']
|
|
239
|
+
assert df['Title'].dtype == 'object' # string in pandas
|
|
240
|
+
assert df['IsChannel'].dtype == 'bool'
|
|
241
|
+
print("✓ Column data types are correct")
|
|
242
|
+
|
|
243
|
+
return True
|
|
244
|
+
except Exception as e:
|
|
245
|
+
print(f"✗ Return format test failed: {e}")
|
|
246
|
+
return False
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
async def test_connection_validation_before_list():
|
|
250
|
+
"""Test connection validation before listing groups"""
|
|
251
|
+
print("\nTEST: Connection validation...")
|
|
252
|
+
try:
|
|
253
|
+
tg = TgData()
|
|
254
|
+
|
|
255
|
+
# Test connection validation
|
|
256
|
+
is_valid = await tg.validate_connection()
|
|
257
|
+
assert isinstance(is_valid, bool)
|
|
258
|
+
print(f"✓ Connection validation returned: {is_valid}")
|
|
259
|
+
|
|
260
|
+
# Test health check
|
|
261
|
+
health = await tg.health_check()
|
|
262
|
+
assert isinstance(health, dict)
|
|
263
|
+
assert 'primary_connection' in health
|
|
264
|
+
print("✓ Health check completed")
|
|
265
|
+
|
|
266
|
+
return True
|
|
267
|
+
except Exception as e:
|
|
268
|
+
print(f"✗ Connection validation test failed: {e}")
|
|
269
|
+
return False
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
async def test_list_groups_error_handling():
|
|
273
|
+
"""Test error handling in list_groups"""
|
|
274
|
+
print("\nTEST: List groups error handling...")
|
|
275
|
+
try:
|
|
276
|
+
# Test with invalid config path
|
|
277
|
+
tg = TgData(config_path="non_existent_config.ini")
|
|
278
|
+
|
|
279
|
+
try:
|
|
280
|
+
groups = await tg.list_groups()
|
|
281
|
+
# If this succeeds, we have a fallback config
|
|
282
|
+
print("✓ Handled missing config gracefully")
|
|
283
|
+
except Exception as config_error:
|
|
284
|
+
# This is also acceptable - proper error handling
|
|
285
|
+
print(f"✓ Raised appropriate error for missing config: {type(config_error).__name__}")
|
|
286
|
+
|
|
287
|
+
return True
|
|
288
|
+
except Exception as e:
|
|
289
|
+
print(f"✗ Error handling test failed: {e}")
|
|
290
|
+
return False
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
async def test_list_groups_with_mock_data():
|
|
294
|
+
"""Test processing of group list data"""
|
|
295
|
+
print("\nTEST: Processing group list data...")
|
|
296
|
+
try:
|
|
297
|
+
# Simulate what list_groups would return
|
|
298
|
+
mock_data = pd.DataFrame([
|
|
299
|
+
{'GroupID': 1001, 'Title': 'Family Chat', 'Username': None, 'IsChannel': False, 'IsMegagroup': False, 'ParticipantsCount': 5},
|
|
300
|
+
{'GroupID': 1002, 'Title': 'Work Team', 'Username': '@workteam', 'IsChannel': False, 'IsMegagroup': True, 'ParticipantsCount': 50},
|
|
301
|
+
{'GroupID': 1003, 'Title': 'News Channel', 'Username': '@newschan', 'IsChannel': True, 'IsMegagroup': False, 'ParticipantsCount': 10000},
|
|
302
|
+
])
|
|
303
|
+
|
|
304
|
+
# Test filtering capabilities
|
|
305
|
+
# Only groups (not channels)
|
|
306
|
+
groups_only = mock_data[~mock_data['IsChannel']]
|
|
307
|
+
assert len(groups_only) == 2
|
|
308
|
+
print("✓ Can filter groups from channels")
|
|
309
|
+
|
|
310
|
+
# Only channels
|
|
311
|
+
channels_only = mock_data[mock_data['IsChannel']]
|
|
312
|
+
assert len(channels_only) == 1
|
|
313
|
+
print("✓ Can filter channels")
|
|
314
|
+
|
|
315
|
+
# Groups with username
|
|
316
|
+
with_username = mock_data[mock_data['Username'].notna()]
|
|
317
|
+
assert len(with_username) == 2
|
|
318
|
+
print("✓ Can filter by username presence")
|
|
319
|
+
|
|
320
|
+
# Large groups
|
|
321
|
+
large_groups = mock_data[mock_data['ParticipantsCount'] > 100]
|
|
322
|
+
assert len(large_groups) == 1
|
|
323
|
+
print("✓ Can filter by participant count")
|
|
324
|
+
|
|
325
|
+
return True
|
|
326
|
+
except Exception as e:
|
|
327
|
+
print(f"✗ Data processing test failed: {e}")
|
|
328
|
+
return False
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
async def test_performance_considerations():
|
|
332
|
+
"""Test performance-related aspects of list_groups"""
|
|
333
|
+
print("\nTEST: Performance considerations...")
|
|
334
|
+
try:
|
|
335
|
+
import time
|
|
336
|
+
|
|
337
|
+
tg = TgData()
|
|
338
|
+
|
|
339
|
+
# Measure initialization time
|
|
340
|
+
start = time.time()
|
|
341
|
+
tg2 = TgData()
|
|
342
|
+
init_time = time.time() - start
|
|
343
|
+
print(f"✓ Initialization time: {init_time:.3f}s")
|
|
344
|
+
assert init_time < 1.0 # Should be fast
|
|
345
|
+
|
|
346
|
+
# Test that list_groups would use single API call
|
|
347
|
+
# (actual test would measure API call time)
|
|
348
|
+
print("✓ list_groups designed for single API call")
|
|
349
|
+
|
|
350
|
+
return True
|
|
351
|
+
except Exception as e:
|
|
352
|
+
print(f"✗ Performance test failed: {e}")
|
|
353
|
+
return False
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
async def main():
|
|
357
|
+
"""Run all list group chats tests"""
|
|
358
|
+
print("List Group Chats Tests")
|
|
359
|
+
print("=" * 50)
|
|
360
|
+
print("Testing the bridge between connection and full group operations")
|
|
361
|
+
print("=" * 50)
|
|
362
|
+
|
|
363
|
+
tests = [
|
|
364
|
+
test_basic_initialization,
|
|
365
|
+
test_list_groups_method_exists,
|
|
366
|
+
test_list_and_show_groups,
|
|
367
|
+
test_list_groups_return_format,
|
|
368
|
+
test_connection_validation_before_list,
|
|
369
|
+
test_list_groups_error_handling,
|
|
370
|
+
test_list_groups_with_mock_data,
|
|
371
|
+
test_performance_considerations
|
|
372
|
+
]
|
|
373
|
+
|
|
374
|
+
results = []
|
|
375
|
+
for test in tests:
|
|
376
|
+
try:
|
|
377
|
+
result = await test()
|
|
378
|
+
results.append(result)
|
|
379
|
+
except Exception as e:
|
|
380
|
+
print(f"✗ Test failed with error: {e}")
|
|
381
|
+
import traceback
|
|
382
|
+
traceback.print_exc()
|
|
383
|
+
results.append(False)
|
|
384
|
+
|
|
385
|
+
# Summary
|
|
386
|
+
print("\nSummary")
|
|
387
|
+
print("=" * 50)
|
|
388
|
+
passed = sum(results)
|
|
389
|
+
total = len(results)
|
|
390
|
+
print(f"Passed: {passed}/{total}")
|
|
391
|
+
|
|
392
|
+
if passed == total:
|
|
393
|
+
print("✓ All list group chats tests passed!")
|
|
394
|
+
print("\nKey validations:")
|
|
395
|
+
print(" - TgData initializes correctly")
|
|
396
|
+
print(" - list_groups method is available and async")
|
|
397
|
+
print(" - Proper DataFrame format with expected columns")
|
|
398
|
+
print(" - Error handling for authentication issues")
|
|
399
|
+
print(" - Data filtering capabilities work")
|
|
400
|
+
return 0
|
|
401
|
+
else:
|
|
402
|
+
print("✗ Some tests failed")
|
|
403
|
+
return 1
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
if __name__ == "__main__":
|
|
407
|
+
exit_code = asyncio.run(main())
|
|
408
|
+
sys.exit(exit_code)
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Test: Get Message Count
|
|
3
|
+
Demonstrates getting total message count without fetching all messages
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
# to run python -m tgdata.smoke_tests.test_02_get_message_count
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
import os
|
|
10
|
+
import sys
|
|
11
|
+
|
|
12
|
+
# Add parent directory to path
|
|
13
|
+
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
|
14
|
+
|
|
15
|
+
from tgdata import TgData
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
async def test_get_message_count():
|
|
19
|
+
"""Get message count for a group"""
|
|
20
|
+
|
|
21
|
+
# Configuration
|
|
22
|
+
group_id = int(os.environ.get('TEST_GROUP_ID', '1670178185')) # Bitcoinsensus by default
|
|
23
|
+
|
|
24
|
+
print(f"Getting message count for group {group_id}")
|
|
25
|
+
print("-" * 40)
|
|
26
|
+
|
|
27
|
+
try:
|
|
28
|
+
# Initialize TgData
|
|
29
|
+
tg = TgData("config.ini")
|
|
30
|
+
|
|
31
|
+
# Get message count
|
|
32
|
+
print("\nFetching count...")
|
|
33
|
+
count = await tg.get_message_count(group_id=group_id)
|
|
34
|
+
|
|
35
|
+
print(f"\n✅ Total messages in group: {count:,}")
|
|
36
|
+
|
|
37
|
+
# Also test with multiple groups if available
|
|
38
|
+
print("\nChecking all accessible groups...")
|
|
39
|
+
groups = await tg.list_groups()
|
|
40
|
+
|
|
41
|
+
if not groups.empty:
|
|
42
|
+
print(f"\nMessage counts for all groups:")
|
|
43
|
+
print("-" * 40)
|
|
44
|
+
|
|
45
|
+
for _, group in groups.head(5).iterrows(): # Limit to first 5 groups
|
|
46
|
+
try:
|
|
47
|
+
count = await tg.get_message_count(group_id=group['GroupID'])
|
|
48
|
+
print(f"{group['Title'][:30]:30} : {count:,} messages")
|
|
49
|
+
except Exception as e:
|
|
50
|
+
print(f"{group['Title'][:30]:30} : Error - {str(e)[:20]}")
|
|
51
|
+
|
|
52
|
+
return True
|
|
53
|
+
|
|
54
|
+
except Exception as e:
|
|
55
|
+
print(f"❌ Error: {e}")
|
|
56
|
+
return False
|
|
57
|
+
finally:
|
|
58
|
+
await tg.close()
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
async def main():
|
|
62
|
+
"""Run the test"""
|
|
63
|
+
print("Test: Get Message Count")
|
|
64
|
+
print("=" * 40)
|
|
65
|
+
|
|
66
|
+
success = await test_get_message_count()
|
|
67
|
+
|
|
68
|
+
if not success:
|
|
69
|
+
sys.exit(1)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
if __name__ == "__main__":
|
|
73
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Test: Get All Messages
|
|
3
|
+
Demonstrates extracting ALL messages from a group and saving to CSV
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
# python -m tgdata.smoke_tests.test_get_all_messages
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
import os
|
|
10
|
+
import sys
|
|
11
|
+
|
|
12
|
+
# Add parent directory to path
|
|
13
|
+
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
|
14
|
+
|
|
15
|
+
from tgdata import TgData
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
async def test_get_all_messages():
|
|
19
|
+
"""Get all messages from a group and save to CSV"""
|
|
20
|
+
|
|
21
|
+
# Configuration
|
|
22
|
+
group_id = int(os.environ.get('TEST_GROUP_ID', '1670178185')) # Bitcoinsensus by default
|
|
23
|
+
output_csv = 'all_messages.csv'
|
|
24
|
+
|
|
25
|
+
print(f"Getting ALL messages from group {group_id}")
|
|
26
|
+
print(f"Output file: {output_csv}")
|
|
27
|
+
print("-" * 50)
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
# Initialize TgData
|
|
31
|
+
tg = TgData("config.ini")
|
|
32
|
+
|
|
33
|
+
# Get ALL messages (after_id=0 means start from beginning)
|
|
34
|
+
print("\nExtracting messages...")
|
|
35
|
+
|
|
36
|
+
all_messages = await tg.get_messages(
|
|
37
|
+
group_id=group_id,
|
|
38
|
+
after_id=0 # Start from the beginning
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
if not all_messages.empty:
|
|
42
|
+
# Save to CSV
|
|
43
|
+
all_messages.to_csv(output_csv, index=False)
|
|
44
|
+
print(f"\n✅ Saved {len(all_messages)} messages to {output_csv}")
|
|
45
|
+
return True
|
|
46
|
+
else:
|
|
47
|
+
print("No messages found")
|
|
48
|
+
return False
|
|
49
|
+
|
|
50
|
+
except Exception as e:
|
|
51
|
+
print(f"❌ Error: {e}")
|
|
52
|
+
return False
|
|
53
|
+
finally:
|
|
54
|
+
await tg.close()
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
async def main():
|
|
58
|
+
"""Run the test"""
|
|
59
|
+
print("Test: Get All Messages")
|
|
60
|
+
print("-" * 30)
|
|
61
|
+
|
|
62
|
+
success = await test_get_all_messages()
|
|
63
|
+
|
|
64
|
+
if not success:
|
|
65
|
+
sys.exit(1)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
if __name__ == "__main__":
|
|
69
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Test: Get All Messages in Batches
|
|
3
|
+
Demonstrates extracting messages in batches and appending to CSV
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
# to run python -m tgdata.smoke_tests.test_03_get_all_messages_in_batches
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
import os
|
|
10
|
+
import sys
|
|
11
|
+
|
|
12
|
+
# Add parent directory to path
|
|
13
|
+
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
|
14
|
+
|
|
15
|
+
from tgdata import TgData
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
async def test_get_all_messages_in_batches():
|
|
19
|
+
"""Get messages in batches and save to CSV"""
|
|
20
|
+
|
|
21
|
+
# Configuration
|
|
22
|
+
group_id = int(os.environ.get('TEST_GROUP_ID', '1670178185')) # Bitcoinsensus by default
|
|
23
|
+
output_csv = 'all_messages_merged_batches.csv'
|
|
24
|
+
batch_size = 10
|
|
25
|
+
num_batches = 3
|
|
26
|
+
|
|
27
|
+
print(f"Getting messages from group {group_id} in batches")
|
|
28
|
+
print(f"Batch size: {batch_size}")
|
|
29
|
+
print(f"Number of batches: {num_batches}")
|
|
30
|
+
print(f"Output file: {output_csv}")
|
|
31
|
+
print("-" * 50)
|
|
32
|
+
|
|
33
|
+
# Remove existing file if exists
|
|
34
|
+
if os.path.exists(output_csv):
|
|
35
|
+
os.remove(output_csv)
|
|
36
|
+
print(f"Removed existing {output_csv}")
|
|
37
|
+
|
|
38
|
+
try:
|
|
39
|
+
# Initialize TgData
|
|
40
|
+
tg = TgData("config.ini")
|
|
41
|
+
|
|
42
|
+
last_message_id = 0
|
|
43
|
+
total_messages = 0
|
|
44
|
+
|
|
45
|
+
# Process in batches
|
|
46
|
+
for batch_num in range(1, num_batches + 1):
|
|
47
|
+
print(f"\nBatch {batch_num}/{num_batches}:")
|
|
48
|
+
print(f" Getting messages after ID {last_message_id}...")
|
|
49
|
+
|
|
50
|
+
# Get batch of messages
|
|
51
|
+
batch = await tg.get_messages(
|
|
52
|
+
group_id=group_id,
|
|
53
|
+
after_id=last_message_id,
|
|
54
|
+
limit=batch_size
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
if not batch.empty:
|
|
58
|
+
# Write to CSV (append mode after first batch)
|
|
59
|
+
if batch_num == 1:
|
|
60
|
+
batch.to_csv(output_csv, index=False)
|
|
61
|
+
else:
|
|
62
|
+
batch.to_csv(output_csv, mode='a', header=False, index=False)
|
|
63
|
+
|
|
64
|
+
# Update counters
|
|
65
|
+
batch_count = len(batch)
|
|
66
|
+
total_messages += batch_count
|
|
67
|
+
last_message_id = int(batch['MessageId'].max())
|
|
68
|
+
|
|
69
|
+
print(f" Saved {batch_count} messages")
|
|
70
|
+
print(f" Last message ID: {last_message_id}")
|
|
71
|
+
else:
|
|
72
|
+
print(f" No messages found")
|
|
73
|
+
break
|
|
74
|
+
|
|
75
|
+
print(f"\n✅ Total messages saved: {total_messages}")
|
|
76
|
+
print(f"📁 Output file: {output_csv}")
|
|
77
|
+
return True
|
|
78
|
+
|
|
79
|
+
except Exception as e:
|
|
80
|
+
print(f"❌ Error: {e}")
|
|
81
|
+
return False
|
|
82
|
+
finally:
|
|
83
|
+
await tg.close()
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
async def main():
|
|
87
|
+
"""Run the test"""
|
|
88
|
+
print("Test: Get All Messages in Batches")
|
|
89
|
+
print("-" * 40)
|
|
90
|
+
|
|
91
|
+
success = await test_get_all_messages_in_batches()
|
|
92
|
+
|
|
93
|
+
if not success:
|
|
94
|
+
sys.exit(1)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
if __name__ == "__main__":
|
|
98
|
+
asyncio.run(main())
|