slim-S commited on
Commit
132ace4
ยท
1 Parent(s): 259451b

feat: first commit

Browse files
This view is limited to 50 files because it contains too many changes. ย  See raw diff
Files changed (50) hide show
  1. .gitattributes +1 -0
  2. Dockerfile +24 -0
  3. app.py +545 -0
  4. data/pending/text/raid-audio 1300.wav.txt +1 -0
  5. data/pending/text/raid-audio 1302.wav.txt +1 -0
  6. data/pending/text/raid-audio 1303.wav.txt +1 -0
  7. data/pending/text/raid-audio 1304.wav.txt +1 -0
  8. data/pending/text/raid-audio 1305.wav.txt +1 -0
  9. data/pending/text/raid-audio 1306.wav.txt +1 -0
  10. data/pending/text/raid-audio 1307.wav.txt +1 -0
  11. data/pending/text/raid-audio 1308.wav.txt +1 -0
  12. data/pending/text/raid-audio 1309.wav.txt +1 -0
  13. data/pending/text/raid-audio 1310.wav.txt +1 -0
  14. data/pending/text/raid-audio 1311.wav.txt +1 -0
  15. data/pending/text/raid-audio 1312.wav.txt +1 -0
  16. data/pending/text/raid-audio 1313.wav.txt +1 -0
  17. data/pending/text/raid-audio 1314.wav.txt +1 -0
  18. data/pending/text/raid-audio 1315.wav.txt +1 -0
  19. data/pending/text/raid-audio 1316.wav.txt +1 -0
  20. data/pending/text/raid-audio 1317.wav.txt +1 -0
  21. data/pending/text/raid-audio 1318.wav.txt +1 -0
  22. data/pending/text/raid-audio 1319.wav.txt +1 -0
  23. data/pending/text/raid-audio 1320.wav.txt +1 -0
  24. data/pending/text/raid-audio 1321.wav.txt +1 -0
  25. data/pending/text/raid-audio 1322.wav.txt +1 -0
  26. data/pending/text/raid-audio 1323.wav.txt +1 -0
  27. data/pending/text/raid-audio 1324.wav.txt +1 -0
  28. data/pending/text/raid-audio 1325.wav.txt +1 -0
  29. data/pending/text/raid-audio 1326.wav.txt +1 -0
  30. data/pending/text/raid-audio 1327.wav.txt +1 -0
  31. data/pending/text/raid-audio 1328.wav.txt +1 -0
  32. data/pending/text/raid-audio 1329.wav.txt +1 -0
  33. data/pending/text/raid-audio 1330.wav.txt +1 -0
  34. data/pending/text/raid-audio 1331.wav.txt +1 -0
  35. data/pending/text/raid-audio 1332.wav.txt +1 -0
  36. data/pending/text/raid-audio 1333.wav.txt +1 -0
  37. data/pending/text/raid-audio 1334.wav.txt +1 -0
  38. data/pending/text/raid-audio 1335.wav.txt +1 -0
  39. data/pending/text/raid-audio 1336.wav.txt +1 -0
  40. data/pending/text/raid-audio 1337.wav.txt +1 -0
  41. data/pending/text/raid-audio 1338.wav.txt +1 -0
  42. data/pending/text/raid-audio 1339.wav.txt +1 -0
  43. data/pending/text/raid-audio 1340.wav.txt +1 -0
  44. data/pending/text/raid-audio 1341.wav.txt +1 -0
  45. data/pending/text/raid-audio 1342.wav.txt +1 -0
  46. data/pending/text/raid-audio 1343.wav.txt +1 -0
  47. data/pending/text/raid-audio 1344.wav.txt +1 -0
  48. data/pending/text/raid-audio 1345.wav.txt +1 -0
  49. data/pending/text/raid-audio 1346.wav.txt +1 -0
  50. data/pending/text/raid-audio 1347.wav.txt +1 -0
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ data/pending/voice/*.wav filter=lfs diff=lfs merge=lfs -text
Dockerfile ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use a Python 3.10 base image
2
+ FROM python:3.10-slim
3
+
4
+ # Set working directory
5
+ WORKDIR ./
6
+
7
+ # 1. First install dependencies as root
8
+ COPY requirements.txt .
9
+ RUN pip install --no-cache-dir -r requirements.txt
10
+
11
+ # 2. Create non-root user and data directories
12
+ RUN useradd -m -u 1000 appuser && \
13
+ mkdir -p /data/processed/text /data/processed/voice \
14
+ /data/pending/text /data/pending/voice && \
15
+ chown -R appuser:appuser /data
16
+
17
+ # 3. Switch to non-root user
18
+ USER appuser
19
+
20
+ # 4. Copy app files (now owned by appuser)
21
+ COPY --chown=appuser . .
22
+
23
+ # Change the command to use main.py instead of app.py
24
+ CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
app.py ADDED
@@ -0,0 +1,545 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import logging
3
+ import shutil
4
+ import re
5
+ import asyncio
6
+ import signal
7
+ import traceback
8
+ import stat
9
+ from telegram import Update
10
+ from telegram.ext import Application, CommandHandler, MessageHandler, filters, ContextTypes, ConversationHandler
11
+ from typing import Dict, Set, Tuple, Optional
12
+
13
+ # Enable more detailed logging
14
+ logging.basicConfig(
15
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
16
+ level=logging.DEBUG # Changed to DEBUG for more details
17
+ )
18
+ logger = logging.getLogger(__name__)
19
+
20
+ # Add specific logger for telegram
21
+ telegram_logger = logging.getLogger('telegram')
22
+ telegram_logger.setLevel(logging.DEBUG)
23
+
24
+ # Define conversation states
25
+ AWAITING_CORRECTION = 1
26
+
27
+ # Store active tasks per user
28
+ user_tasks = {} # {user_id: (voice_filename, text_filename, seq_num)}
29
+
30
+ # Track chunks being processed
31
+ chunks_in_process = set() # set of sequence numbers that are currently being processed
32
+
33
+ # Directory paths
34
+ DATA_DIR = os.environ.get('DATA_DIR', 'data')
35
+ PENDING_DIR = os.path.join(DATA_DIR, 'pending')
36
+ PENDING_VOICE_DIR = os.path.join(PENDING_DIR, 'voice')
37
+ PENDING_TEXT_DIR = os.path.join(PENDING_DIR, 'text')
38
+ PROCESSED_DIR = os.path.join(DATA_DIR, 'processed')
39
+ PROCESSED_VOICE_DIR = os.path.join(PROCESSED_DIR, 'voice')
40
+ PROCESSED_TEXT_DIR = os.path.join(PROCESSED_DIR, 'text')
41
+
42
+ # Ensure directories exist with proper permissions
43
+ def ensure_directory_with_permissions(directory):
44
+ """Create directory if it doesn't exist and set permissions."""
45
+ try:
46
+ if not os.path.exists(directory):
47
+ os.makedirs(directory, exist_ok=True)
48
+ # Set permissions: read/write/execute for everyone
49
+ os.chmod(directory, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) # 0777 permissions
50
+ logger.info(f"Directory ensured with permissions: {directory}")
51
+ except Exception as e:
52
+ logger.error(f"Error setting permissions for {directory}: {str(e)}")
53
+
54
+ # Initialize directories with proper permissions
55
+ ensure_directory_with_permissions(PENDING_VOICE_DIR)
56
+ ensure_directory_with_permissions(PENDING_TEXT_DIR)
57
+ ensure_directory_with_permissions(PROCESSED_VOICE_DIR)
58
+ ensure_directory_with_permissions(PROCESSED_TEXT_DIR)
59
+
60
+ # Global application variable
61
+ application = None
62
+
63
+ def extract_sequence_number(filename: str) -> Optional[int]:
64
+ """Extract sequence number from filename, e.g., 'Sound 100.wav' -> 100."""
65
+ match = re.search(r'(\d+)', filename)
66
+ if match:
67
+ return int(match.group(1))
68
+ return None
69
+
70
+ def get_files_by_sequence_number():
71
+ """Create dictionaries mapping sequence numbers to filenames."""
72
+ voice_files = {}
73
+ text_files = {}
74
+
75
+ # Map voice files to sequence numbers
76
+ if os.path.exists(PENDING_VOICE_DIR):
77
+ for filename in os.listdir(PENDING_VOICE_DIR):
78
+ if filename.endswith('.wav'):
79
+ seq_num = extract_sequence_number(filename)
80
+ if seq_num is not None:
81
+ voice_files[seq_num] = filename
82
+
83
+ # Map text files to sequence numbers
84
+ if os.path.exists(PENDING_TEXT_DIR):
85
+ for filename in os.listdir(PENDING_TEXT_DIR):
86
+ if filename.endswith('.txt'):
87
+ seq_num = extract_sequence_number(filename)
88
+ if seq_num is not None:
89
+ text_files[seq_num] = filename
90
+
91
+ return voice_files, text_files
92
+
93
+ async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
94
+ """Send a welcome message when the /start command is issued."""
95
+ logger.info(f"Start command received from user {update.effective_user.id}")
96
+ try:
97
+ await update.message.reply_text(
98
+ "Welcome to the Algerian Darija Transcription Correction Bot!\n\n"
99
+ "Use /correct to start correcting a transcription.\n"
100
+ "Use /cancel to cancel the current correction task."
101
+ )
102
+ logger.info("Start message sent successfully")
103
+ except Exception as e:
104
+ logger.error(f"Error sending start message: {e}")
105
+
106
+ async def get_next_available_chunk() -> Tuple[Optional[str], Optional[str], Optional[int]]:
107
+ """Find the next available audio chunk that isn't being processed.
108
+ Returns (voice_filename, text_filename, sequence_number) or (None, None, None)"""
109
+ voice_files, text_files = get_files_by_sequence_number()
110
+
111
+ # Find sequence numbers that exist in both voice and text files
112
+ common_seq_nums = set(voice_files.keys()).intersection(set(text_files.keys()))
113
+
114
+ # Filter out sequence numbers that are already being processed
115
+ available_seq_nums = [seq_num for seq_num in common_seq_nums if seq_num not in chunks_in_process]
116
+
117
+ logger.debug(f"Available sequence numbers: {available_seq_nums}")
118
+
119
+ if available_seq_nums:
120
+ # Get the first available sequence number
121
+ seq_num = min(available_seq_nums)
122
+ return voice_files[seq_num], text_files[seq_num], seq_num
123
+
124
+ logger.debug("No available chunks found")
125
+ return None, None, None
126
+
127
+ async def correct(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
128
+ """Start the correction process."""
129
+ logger.info(f"Correct command received from user {update.effective_user.id}")
130
+ user_id = update.effective_user.id
131
+
132
+ # Check if user already has an active task
133
+ if user_id in user_tasks:
134
+ await update.message.reply_text("You already have an active correction task. Please finish it or use /cancel.")
135
+ return AWAITING_CORRECTION
136
+
137
+ # Ensure directory paths exist with proper permissions
138
+ ensure_directory_with_permissions(PENDING_VOICE_DIR)
139
+ ensure_directory_with_permissions(PENDING_TEXT_DIR)
140
+
141
+ # Log directory contents for debugging
142
+ logger.debug(f"PENDING_VOICE_DIR contents: {os.listdir(PENDING_VOICE_DIR)}")
143
+ logger.debug(f"PENDING_TEXT_DIR contents: {os.listdir(PENDING_TEXT_DIR)}")
144
+
145
+ # Get next available chunk
146
+ voice_filename, text_filename, seq_num = await get_next_available_chunk()
147
+
148
+ if not voice_filename or not text_filename:
149
+ await update.message.reply_text("No pending transcriptions available at the moment. Please try again later.")
150
+ return ConversationHandler.END
151
+
152
+ # Mark chunk as being processed
153
+ chunks_in_process.add(seq_num)
154
+ user_tasks[user_id] = (voice_filename, text_filename, seq_num)
155
+
156
+ # Send audio file
157
+ audio_path = os.path.join(PENDING_VOICE_DIR, voice_filename)
158
+
159
+ # Check if audio file exists
160
+ if not os.path.exists(audio_path):
161
+ logger.error(f"Audio file not found: {audio_path}")
162
+ await update.message.reply_text("Error: Audio file not found. Please try again.")
163
+
164
+ # Clean up
165
+ chunks_in_process.remove(seq_num)
166
+ del user_tasks[user_id]
167
+ return ConversationHandler.END
168
+
169
+ # Read the original transcription to send as reference
170
+ text_path = os.path.join(PENDING_TEXT_DIR, text_filename)
171
+ if not os.path.exists(text_path):
172
+ logger.error(f"Text file not found: {text_path}")
173
+ await update.message.reply_text("Error: Text file not found. Please try again.")
174
+
175
+ # Clean up
176
+ chunks_in_process.remove(seq_num)
177
+ del user_tasks[user_id]
178
+ return ConversationHandler.END
179
+
180
+ try:
181
+ with open(text_path, 'r', encoding='utf-8') as f:
182
+ original_text = f.read().strip()
183
+ except Exception as e:
184
+ logger.error(f"Error reading text file: {e}")
185
+ await update.message.reply_text(f"Error reading transcription file. Please try again.")
186
+
187
+ # Clean up
188
+ chunks_in_process.remove(seq_num)
189
+ del user_tasks[user_id]
190
+ return ConversationHandler.END
191
+
192
+ # Send audio and original text
193
+ try:
194
+ with open(audio_path, 'rb') as audio_file:
195
+ await update.message.reply_voice(voice=audio_file)
196
+
197
+ await update.message.reply_text(
198
+ f"Please listen to the audio and correct the transcription.\n\n"
199
+ f"Original transcription:\n{original_text}\n\n"
200
+ f"Please send your corrected version."
201
+ )
202
+
203
+ logger.info(f"Successfully sent audio and text to user {user_id}")
204
+ return AWAITING_CORRECTION
205
+ except Exception as e:
206
+ logger.error(f"Error sending audio or text: {e}")
207
+ await update.message.reply_text("Error sending audio. Please try again.")
208
+
209
+ # Clean up
210
+ chunks_in_process.remove(seq_num)
211
+ del user_tasks[user_id]
212
+ return ConversationHandler.END
213
+
214
+ async def save_correction(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
215
+ """Save the user's correction."""
216
+ user_id = update.effective_user.id
217
+
218
+ if user_id not in user_tasks:
219
+ await update.message.reply_text("You don't have an active correction task. Use /correct to start one.")
220
+ return ConversationHandler.END
221
+
222
+ voice_filename, text_filename, seq_num = user_tasks[user_id]
223
+ corrected_text = update.message.text
224
+
225
+ # Get original file paths
226
+ original_txt_path = os.path.join(PENDING_TEXT_DIR, text_filename)
227
+ original_wav_path = os.path.join(PENDING_VOICE_DIR, voice_filename)
228
+
229
+ # Create user-specific directories in processed folders with proper permissions
230
+ user_processed_voice_dir = os.path.join(PROCESSED_VOICE_DIR, f"user_{user_id}")
231
+ user_processed_text_dir = os.path.join(PROCESSED_TEXT_DIR, f"user_{user_id}")
232
+
233
+ # Log the directory paths for debugging
234
+ logger.debug(f"Creating directories: {user_processed_voice_dir}, {user_processed_text_dir}")
235
+
236
+ try:
237
+ # Ensure directories with proper permissions
238
+ ensure_directory_with_permissions(user_processed_voice_dir)
239
+ ensure_directory_with_permissions(user_processed_text_dir)
240
+
241
+ # Move files to processed directory with corrected text
242
+ processed_txt_path = os.path.join(user_processed_text_dir, text_filename)
243
+ processed_wav_path = os.path.join(user_processed_voice_dir, voice_filename)
244
+
245
+ # Log file paths for debugging
246
+ logger.debug(f"Original text path: {original_txt_path}, exists: {os.path.exists(original_txt_path)}")
247
+ logger.debug(f"Original wav path: {original_wav_path}, exists: {os.path.exists(original_wav_path)}")
248
+ logger.debug(f"Processed text path: {processed_txt_path}")
249
+ logger.debug(f"Processed wav path: {processed_wav_path}")
250
+
251
+ # Save corrected text first
252
+ logger.debug(f"Saving corrected text to {processed_txt_path}")
253
+ with open(processed_txt_path, 'w', encoding='utf-8') as f:
254
+ f.write(corrected_text)
255
+ # Set file permissions
256
+ os.chmod(processed_txt_path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IWGRP | stat.S_IROTH | stat.S_IWOTH)
257
+
258
+ # Copy wav file - verify it exists first
259
+ if os.path.exists(original_wav_path):
260
+ logger.debug(f"Copying wav file from {original_wav_path} to {processed_wav_path}")
261
+ shutil.copy2(original_wav_path, processed_wav_path)
262
+ # Set file permissions
263
+ os.chmod(processed_wav_path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IWGRP | stat.S_IROTH | stat.S_IWOTH)
264
+ else:
265
+ logger.error(f"Original wav file does not exist at {original_wav_path}")
266
+ await update.message.reply_text("Error: Original audio file not found. Please try again.")
267
+ return AWAITING_CORRECTION
268
+
269
+ # Verify processed files exist
270
+ logger.debug(f"Verifying processed files: text exists: {os.path.exists(processed_txt_path)}, wav exists: {os.path.exists(processed_wav_path)}")
271
+
272
+ # Remove from pending directory - be careful to check if they exist first
273
+ if os.path.exists(original_txt_path):
274
+ logger.debug(f"Removing original text file at {original_txt_path}")
275
+ os.remove(original_txt_path)
276
+ else:
277
+ logger.warning(f"Could not remove original text file as it doesn't exist at {original_txt_path}")
278
+
279
+ if os.path.exists(original_wav_path):
280
+ logger.debug(f"Removing original wav file at {original_wav_path}")
281
+ os.remove(original_wav_path)
282
+ else:
283
+ logger.warning(f"Could not remove original wav file as it doesn't exist at {original_wav_path}")
284
+
285
+ # Remove from active tasks
286
+ logger.debug(f"Removing seq_num {seq_num} from chunks_in_process")
287
+ chunks_in_process.remove(seq_num)
288
+
289
+ logger.debug(f"Removing user {user_id} from user_tasks")
290
+ del user_tasks[user_id]
291
+
292
+ logger.info(f"Successfully saved correction for user {user_id}")
293
+ await update.message.reply_text(
294
+ "Thank you! Your correction has been saved.\n"
295
+ "Use /correct to receive another transcription task."
296
+ )
297
+
298
+ return ConversationHandler.END
299
+ except Exception as e:
300
+ # Enhanced error logging with stack trace
301
+ error_msg = f"Error saving correction: {str(e)}"
302
+ logger.error(error_msg)
303
+ logger.error(traceback.format_exc())
304
+
305
+ # Try to provide more specific error messages
306
+ if "Permission denied" in str(e):
307
+ await update.message.reply_text("Error: Permission denied while saving files. Please contact the administrator.")
308
+ elif "No such file or directory" in str(e):
309
+ await update.message.reply_text("Error: File not found. The system couldn't find one of the files.")
310
+ else:
311
+ await update.message.reply_text(f"Error saving your correction: {str(e)}. Please try again.")
312
+
313
+ return AWAITING_CORRECTION
314
+
315
+ async def cancel(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
316
+ """Cancel the current correction task."""
317
+ user_id = update.effective_user.id
318
+
319
+ if user_id in user_tasks:
320
+ _, _, seq_num = user_tasks[user_id]
321
+ chunks_in_process.remove(seq_num)
322
+ del user_tasks[user_id]
323
+
324
+ await update.message.reply_text("Task cancelled. Use /correct to start a new one.")
325
+ else:
326
+ await update.message.reply_text("You don't have an active task to cancel.")
327
+
328
+ return ConversationHandler.END
329
+
330
+ async def status(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
331
+ """Show status of pending and processed chunks."""
332
+ logger.info(f"Status command received from user {update.effective_user.id}")
333
+
334
+ # Count pending files
335
+ pending_voice_count = len([f for f in os.listdir(PENDING_VOICE_DIR) if f.endswith('.wav')])
336
+ pending_text_count = len([f for f in os.listdir(PENDING_TEXT_DIR) if f.endswith('.txt')])
337
+
338
+ # Get sequence numbers
339
+ voice_files, text_files = get_files_by_sequence_number()
340
+ matching_pairs = len(set(voice_files.keys()).intersection(set(text_files.keys())))
341
+
342
+ # Count processed files
343
+ processed_voice_count = 0
344
+ processed_text_count = 0
345
+
346
+ # Count voice files
347
+ for root, dirs, files in os.walk(PROCESSED_VOICE_DIR):
348
+ processed_voice_count += len([f for f in files if f.endswith('.wav')])
349
+
350
+ # Count text files
351
+ for root, dirs, files in os.walk(PROCESSED_TEXT_DIR):
352
+ processed_text_count += len([f for f in files if f.endswith('.txt')])
353
+
354
+ active_tasks = len(user_tasks)
355
+
356
+ # Check file permissions
357
+ permissions_info = ""
358
+ try:
359
+ # Check if we can write to the processed directories
360
+ can_write_voice = os.access(PROCESSED_VOICE_DIR, os.W_OK)
361
+ can_write_text = os.access(PROCESSED_TEXT_DIR, os.W_OK)
362
+ permissions_info = f"\nโ€ข Write permissions: Voice: {can_write_voice}, Text: {can_write_text}"
363
+ except Exception as e:
364
+ permissions_info = f"\nโ€ข Error checking permissions: {str(e)}"
365
+
366
+ await update.message.reply_text(
367
+ f"๐Ÿ“Š Transcription Status:\n"
368
+ f"โ€ข Pending voice files: {pending_voice_count}\n"
369
+ f"โ€ข Pending text files: {pending_text_count}\n"
370
+ f"โ€ข Matching pending pairs: {matching_pairs}\n"
371
+ f"โ€ข Processed voice files: {processed_voice_count}\n"
372
+ f"โ€ข Processed text files: {processed_text_count}\n"
373
+ f"โ€ข Active tasks: {active_tasks}{permissions_info}"
374
+ )
375
+
376
+ async def debug_info(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
377
+ """Command to show debug information about directories and permissions."""
378
+ user_id = update.effective_user.id
379
+
380
+ # Check directory structure
381
+ debug_info = [
382
+ "๐Ÿ“ Directory Check:",
383
+ f"โ€ข DATA_DIR: {DATA_DIR}, exists: {os.path.exists(DATA_DIR)}",
384
+ f"โ€ข PENDING_DIR: {PENDING_DIR}, exists: {os.path.exists(PENDING_DIR)}",
385
+ f"โ€ข PENDING_VOICE_DIR: {PENDING_VOICE_DIR}, exists: {os.path.exists(PENDING_VOICE_DIR)}",
386
+ f"โ€ข PENDING_TEXT_DIR: {PENDING_TEXT_DIR}, exists: {os.path.exists(PENDING_TEXT_DIR)}",
387
+ f"โ€ข PROCESSED_DIR: {PROCESSED_DIR}, exists: {os.path.exists(PROCESSED_DIR)}",
388
+ f"โ€ข PROCESSED_VOICE_DIR: {PROCESSED_VOICE_DIR}, exists: {os.path.exists(PROCESSED_VOICE_DIR)}",
389
+ f"โ€ข PROCESSED_TEXT_DIR: {PROCESSED_TEXT_DIR}, exists: {os.path.exists(PROCESSED_TEXT_DIR)}",
390
+ ]
391
+
392
+ # Check permissions
393
+ try:
394
+ permission_info = [
395
+ "๐Ÿ”‘ Permission Check:",
396
+ f"โ€ข PENDING_VOICE_DIR writable: {os.access(PENDING_VOICE_DIR, os.W_OK)}",
397
+ f"โ€ข PENDING_TEXT_DIR writable: {os.access(PENDING_TEXT_DIR, os.W_OK)}",
398
+ f"โ€ข PROCESSED_VOICE_DIR writable: {os.access(PROCESSED_VOICE_DIR, os.W_OK)}",
399
+ f"โ€ข PROCESSED_TEXT_DIR writable: {os.access(PROCESSED_TEXT_DIR, os.W_OK)}",
400
+ ]
401
+ debug_info.extend(permission_info)
402
+ except Exception as e:
403
+ debug_info.append(f"Error checking permissions: {str(e)}")
404
+
405
+ # Check active tasks
406
+ task_info = [
407
+ "๐Ÿ“‹ Active Tasks:",
408
+ f"โ€ข Number of active users: {len(user_tasks)}",
409
+ f"โ€ข Active chunks: {len(chunks_in_process)}",
410
+ ]
411
+ debug_info.extend(task_info)
412
+
413
+ await update.message.reply_text("\n".join(debug_info))
414
+
415
+ async def test_handler(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
416
+ """Test handler to verify bot is receiving messages."""
417
+ logger.info(f"Test command received from user {update.effective_user.id}")
418
+ await update.message.reply_text("Bot is working! โœ…")
419
+
420
+ async def setup_application():
421
+ """Setup the Application with all handlers."""
422
+ global application
423
+
424
+ # Get the token from environment variable
425
+ token = os.environ.get("TELEGRAM_TOKEN")
426
+ if not token:
427
+ logger.error("TELEGRAM_TOKEN environment variable not set!")
428
+ return None
429
+
430
+ logger.info(f"Setting up bot with token: {token[:10]}...")
431
+
432
+ # Create the Application with additional debugging
433
+ application = Application.builder().token(token).build()
434
+
435
+ # Add a test handler first
436
+ application.add_handler(CommandHandler("test", test_handler))
437
+
438
+ # Add conversation handler for correction workflow
439
+ conv_handler = ConversationHandler(
440
+ entry_points=[CommandHandler("correct", correct)],
441
+ states={
442
+ AWAITING_CORRECTION: [
443
+ MessageHandler(filters.TEXT & ~filters.COMMAND, save_correction),
444
+ ],
445
+ },
446
+ fallbacks=[CommandHandler("cancel", cancel)],
447
+ name="correction_conversation",
448
+ )
449
+
450
+ application.add_handler(conv_handler)
451
+
452
+ # Add command handlers
453
+ application.add_handler(CommandHandler("start", start))
454
+ application.add_handler(CommandHandler("status", status))
455
+ application.add_handler(CommandHandler("cancel", cancel))
456
+ application.add_handler(CommandHandler("debug", debug_info)) # New debug command
457
+
458
+ logger.info("All handlers added successfully")
459
+ return application
460
+
461
+ async def start_bot():
462
+ """Start the bot with proper signal handling."""
463
+ global application
464
+
465
+ logger.info("Starting bot setup...")
466
+
467
+ # Create and configure the bot
468
+ app = await setup_application()
469
+ if not app:
470
+ logger.error("Failed to create application")
471
+ return
472
+
473
+ logger.info("Application created successfully")
474
+
475
+ # Start the Bot
476
+ try:
477
+ await app.initialize()
478
+ logger.info("Application initialized")
479
+
480
+ await app.start()
481
+ logger.info("Application started")
482
+
483
+ # Start polling for updates
484
+ await app.updater.start_polling(drop_pending_updates=False)
485
+ logger.info("Started polling for updates")
486
+
487
+ except Exception as e:
488
+ logger.error(f"Error starting bot: {e}")
489
+ return
490
+
491
+ # Setup signal handlers for graceful shutdown
492
+ loop = asyncio.get_event_loop()
493
+
494
+ for signal_name in ('SIGINT', 'SIGTERM'):
495
+ try:
496
+ loop.add_signal_handler(
497
+ getattr(signal, signal_name),
498
+ lambda s=signal_name: asyncio.create_task(shutdown(s))
499
+ )
500
+ except NotImplementedError:
501
+ # Windows doesn't support this
502
+ logger.info("Signal handlers not supported on this platform")
503
+ pass
504
+
505
+ try:
506
+ # Just run forever until interrupted
507
+ logger.info("Bot is now running and waiting for messages...")
508
+ await asyncio.Event().wait()
509
+ finally:
510
+ # Ensure the bot is properly shut down
511
+ await shutdown("Manual")
512
+
513
+ async def shutdown(signal_type):
514
+ """Cleanup tasks tied to the service's shutdown."""
515
+ global application
516
+
517
+ logger.info(f"Received exit signal {signal_type}...")
518
+
519
+ # Stop the bot
520
+ if application:
521
+ logger.info("Stopping application...")
522
+ await application.updater.stop()
523
+ await application.stop()
524
+ await application.shutdown()
525
+
526
+ # Cancel all running tasks
527
+ tasks = [t for t in asyncio.all_tasks() if t is not asyncio.current_task()]
528
+ for task in tasks:
529
+ task.cancel()
530
+
531
+ # Wait for all tasks to be cancelled
532
+ if tasks:
533
+ logger.info(f"Waiting for {len(tasks)} tasks to complete...")
534
+ await asyncio.gather(*tasks, return_exceptions=True)
535
+
536
+ logger.info("Application shutdown complete")
537
+
538
+ async def main():
539
+ """Main function to setup and run the bot."""
540
+ # Setup and start the bot
541
+ await start_bot()
542
+
543
+ # This is the entry point when run as the main process
544
+ if __name__ == "__main__":
545
+ asyncio.run(main())
data/pending/text/raid-audio 1300.wav.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ูŠู‚ุนุฏ ุฏูŠู…ุง ููŠ ู†ูุณ ุงู„ุญุงุฌุฉ ู„ูŠ ู…ุด ูŠู…ู„ุงุญ
data/pending/text/raid-audio 1302.wav.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ู‚ูˆู„ูŠ ู…ุดูŠ ุฃุญุจ ู…ุจุฏู„ูƒ ุฃูƒุชู‚ุฑ ุฃูƒุชู‚ุฑ ุจุณ
data/pending/text/raid-audio 1303.wav.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ู„ุง ุจุฏูŠ ุดูˆูŠุฉ ุจูƒูŠู†ูŠ ุดูˆูŠุฉ ูŠู‚ูˆู„ูƒ leaders are readers ุชู‚ุฑุฃุชูƒ ุชุจุช ุถูŠุงุก ุงู„ุฏู‡ู†
data/pending/text/raid-audio 1304.wav.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ู‚ุฑูŠุฉ ุจู„ูƒ ู…ุง ู‡ูŠ ู…ุฒุฏุงุฏ ุจุงู„ุนุฑุจูŠุฉ ู…ุฒุฏุงุฏ ุญุฏุง ุจู„ูƒู†ุฉ
data/pending/text/raid-audio 1305.wav.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ู†ุฑูŠุฏ ุฃู† ู†ู‚ุฑุฃ ุจุงู„ู„ุบุฉ ุงู„ุฅู†ุฌู„ูŠุฒูŠุฉ ูˆุงู„ูุฑู†ุณูŠุฉ ูƒูƒุชุงุจุฉ ู„ู„ู…ุฏูŠู†ุฉ ู„ูƒูŠ ู†ุนุฑู ูƒุซูŠุฑุงู‹
data/pending/text/raid-audio 1306.wav.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ูƒุชุจ ู„ูŠ ุฃุซุฑ ููŠูƒ ููŠ ุฑูŠู…ุงุŸ ุฃู†ุง ู‚ุฑุฃุช ุจูŠูƒุฒูˆุฒ ูƒุชุจู‡ ู„ุซู„ุงุซุฉ ู„ูŠู‡ ุนุฌุจู†ูŠ ููŠู‡ ู…ูˆ ูƒุงู† ุงู„ุฃู…ูŠุฑ ู‡ุฐุง ูƒุชุงุจูŠ ูƒุงูˆูˆูŠู„
data/pending/text/raid-audio 1307.wav.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ุชุฑุฌู…ุฉ ู†ุงู†ุณูŠ ู‚ู†ู‚ุฑ
data/pending/text/raid-audio 1308.wav.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ุดูƒุฑุง
data/pending/text/raid-audio 1309.wav.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ุงู‡ ุตุญูŠุญ ุงูŠู‡ ุชุนุฌุจูƒ ุจู€ ู…ุงูƒุด ุทุงุญ ุจู€ ุทุญ ุฒุนู…ุฉ ู…ูˆุทุงูˆุถุฉ ูˆุนุงู‚ู„ุฉ ู‚ุฑุงุฑูŠ ููŠ ุงู„ุฃู…ู… ุฃุญุจ ู†ุฏุฎู„ ุจู€ ู†ุฏุฎู„ ุจู€ ู†ุฏุฎู„ ุจู€ ู†ุฏุฎู„ ุจู€
data/pending/text/raid-audio 1310.wav.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ู„ุง ุจุตุฑุงุญุฉ ู„ูƒ ุงู„ุฃู…ูŠุฑ ูƒุด ู…ุง ุชุชู‚ุฑุฃ ุงู„ุฃู…ูŠุฑ ุฃู†ุง ู‚ู„ุชู‡ุง ุฌู…ูŠู„ุฉ ูƒุด ู…ุง ุชุชู‚ุฑุฃ ุงู„ุฃู…ูŠุฑ ุฎูˆ
data/pending/text/raid-audio 1311.wav.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ุงู†ุง ุงุฑูŠุฏ ุงู† ุงุนุฑู ูƒูŠู ูŠู…ูƒู†ู†ูŠ ุงู„ุชู‚ุงุทู‡ู… ููŠ ุงู„ู‚ู†ุงุฉ
data/pending/text/raid-audio 1312.wav.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ุญูƒูŠู†ุง ุดูˆูŠ ุนู„ู‰ ุงู„ุฃู…ูŠุฑ ุญูƒูŠู†ุง
data/pending/text/raid-audio 1313.wav.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ูˆูƒู†ุง ู†ุฏุฑุณ ุงู„ุนู„ู… ุงู„ุณูŠุงุณูŠ ูˆู…ุง ู†ุถุนู‡ ููŠ ุฏู…ู‰ ุฃูุถู„ ูƒุชุงุจ ู‚ุฑุฃุชู‡ ุญุชู‰ ุงู„ุขู† ุงู„ุฃู…ูŠุฑ ู…ุง ู‡ูˆ ูƒุชุงุจู‡ุŸ ู…ูŠูƒุงูˆูŠู„ ู†ุนู… ุงู„ุจู†ูŠุฉ ุงู„ุชูŠ ุชุฑูŠุฏ ุฃู† ุชูƒูˆู† ูุฑูŠุฏุฉ ู…ู†ู‡ุง ุชู‚ุนู‡ุง ุญุงุฌุฉ ุฃูƒุซุฑ ู…ู† ุงู„ุฃู…ูˆุฑ ุณุชุตุจุญ ุณูŠุงุณุฉ ูู„ุณูุฉ
data/pending/text/raid-audio 1314.wav.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ูŠ Whoo ุข ู…ู† ุงู„ุชุญ ๏ฟฝูุช
data/pending/text/raid-audio 1315.wav.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ุงู„ุบูŠุฉ ุชุจุงุฑู„ ุจุณูŠุทุฉ ุณู…ุน ุนู†ุฏูŠ ูˆุงุญุฏุฉ ูู„ุณูุฉ ุจุตุญ ุงู„ููƒุฑ ุชุงุนู‡ ููƒุฑ ุนู… ุจูƒ ุญุชู‰ ููŠ ุงู„ูƒุชุงุจ ุชุงุนู‡
data/pending/text/raid-audio 1316.wav.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ูƒูŠู ูŠู†ุธุฑ ู„ู„ุฃู…ูˆุฑ ุชุนุฏูˆ ู„ูƒ ูุงุฉ ุณู‚ุทุฉ ูƒูุงุฉ ูˆูŠุฌูŠุจ ู„ูƒ ุงู„ุฃู…ุซู„ุฉ ุงู†ุง ุญุงุฌุฉ ู„ูŠ ุนุฌุจุชู†ูŠ ููƒูุงุฉ ูŠุฌูŠุจ ู„ูƒ ููŠ ุงู„ุฃู…ุซู„ุฉ ูˆูŠุญู„ู„ ูุงุฉ
data/pending/text/raid-audio 1317.wav.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ุณุฃุฌู„ุจ ู„ูƒ ู…ุซุงู„ ู„ุฏูˆู‚
data/pending/text/raid-audio 1318.wav.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ูƒูŠู ุฃู†ุฌุญุŸ
data/pending/text/raid-audio 1319.wav.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ูŠู‚ูˆู„ ู„ูƒ ูŠู‚ูˆู„ ู„ู‡ ู…ุง ูŠู‡ุชู…ุด ุงุณูƒ ู„ู„...
data/pending/text/raid-audio 1320.wav.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ูƒูŠู ู‡ูŠ ุงู„ูˆุณูŠู„ุฉ ุงู„ุชูŠ ุณุชู‚ูˆู… ุจู‡ุงุŸ ูƒูŠู ูŠุฎุฑุฌ ุงู„ู…ู…ู‡ูˆู†ุŸ ูƒูŠู ูŠู‚ุถูŠ ุนู„ู‰ ุงู„ุฃุนุฏุงุชุŸ ุจุณุนุงุฏุฉ ุฃู†ุชุŒ ูƒูŠู ุชุณุชุทูŠุน ุงู„ุชุฑุจุท ุจูŠู† ุงู„ุฌุงู†ุจ ุงู„ุฏูŠู†ูŠ ุงู„ุฅู†ุณุงู† ุงู„ู…ุชุฏูŠู†ุŒ ุงู„ุฅู†ุณุงู† ุงู„ุฑูˆุญูŠุŒ ุงู„ุฅู†ุณุงู† ุงู„ุฃู…ู† ุจุงู„ุฑุจุŸ
data/pending/text/raid-audio 1321.wav.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ู…ุน ู…ูŠูƒุงูˆ ููŠู„ูŠ ู„ุง ุชุฃุฎุฐ ุงู„ุฃููƒุงุฑ ู…ุชุถุงุฑุจุฉ ู‡ู†ุง ุนู†ุฏู…ุง ุชู‚ูˆู„ู†ุง ุฃู† ุงู„ุบุงูŠุฉ ุชุจุฑุฏ ุงู„ูˆุณูŠู„ุฉ ู‡ุฐู‡ ุดูŠุก ู„ุง ู†ุคู…ู† ุจู‡ ู„ุง ุฃู†ุง ู„ุง ุฃู‚ูˆู„ ุดูŠุก ุฃู‚ูˆู„ ู„ูƒ ุฃู†ู‡ ูู„ุณูุฉ ูƒู…ุง ู‚ุงู„ู‡ุง ูŠุนุฌุจู†ูŠ ููƒุฑุชู‡ ู„ุง ูŠู…ูƒู†ู†ูŠ ุฃู† ุฃุณุงุนุฏู‡ ูˆุญุชู‰ ู…ูŠูƒุงูˆ ููŠู„ูŠ ููŠ ูƒุชุงุจู‡ ุฐูƒุฑ ุฃู†ู‡ ู†ู‚ุทุฉ ุฃู†ู‡ ุฏูŠู†ูŠ ุนุงูˆู† ู…ุซู„ุงู‹ ูŠุญูƒูŠ ู„ูƒ ุนู† ุงู„ุญุฑุจ ูƒูŠู ู…ู…ู„ูƒุฉ ุชุญูƒู…ู‡ุง ุจู‡ุง ูŠู‚ูˆู„ ู„ูƒ ุฃู†ู‡ ู‡ู†ุงูƒ ุฌุงู†ุจ ุฃุฎู„ุงู‚ูŠ
data/pending/text/raid-audio 1322.wav.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ู„ูƒูŠ ูŠุนุงูˆู† ุงู„ุดุนุจ ูˆูŠูƒูˆู† ู‡ู†ุงูƒ ุญุงุฌุฉ ูˆุงุญุฏุฉ ูˆู…ู‚ุงุฑู†ุฉ ุจูŠู†ู‡ู… ูˆุญูŠุงุชู‡ู… ู„ูŠุณูˆุง ู…ุชู…ุณูƒูŠู† ุจุงู„ุฏูŠู† ู…ู‚ุงุฑู†ุฉ ุจู…ุง ู„ุฏูŠู‡ู… ู…ุชู…ุณูƒูŠู† ุจุงู„ุฏูŠู† ุชุฌุฏูˆู‡ู… ุฃู‚ูˆู‰ ู„ุฏูŠู‡ู… ู‡ุฏู ูŠุชุนุงุฑูƒูˆุง ููŠู‡
data/pending/text/raid-audio 1323.wav.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ุณู…ูˆ ู…ูŠูƒุงูˆูŠู„ ุนู†ุฏู‡ ูƒูˆุชูŠู„ ุชุนุฏูŠู„ ูŠุญุงูุธ ุนู„ู‰ ุงู„ุฏูŠู†
data/pending/text/raid-audio 1324.wav.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ู„ุง ูŠูˆุฌุฏ ู…ุดูƒู„ุฉ ููŠ ุฃู† ุชุฐู‡ุจ ุฅู„ู‰ ูƒุชุงุจ ู…ูŠูƒุงูˆุจูŠู„ุช ูˆ ุชู‚ูˆู… ุจุงู„ุฅู†ู‚ุงุฐ ุณู…ุนูˆุง ู…ุฑุงุช ุญูุงูŠุฒ ู„ููŠุฏ ุชุนุงูˆู†ูƒ
data/pending/text/raid-audio 1325.wav.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ุจุฏูŠุช ู†ุนุฑู ู‡ู†ุง ุงุณูƒูˆ ู‡ุฐุง ูƒููŠ ุฎู…ู… ู‡ุฐุง ูƒููŠ ุฎู…ู… ูƒููŠ ู†ุงุฎุฏ ุงู„ู†ุฌูŠุฑูŠ ุนุจุฏ ุจู†ูุณ ุงู„ุชุฎู…ู… ู‡ุฐุง
data/pending/text/raid-audio 1326.wav.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ุณู…ุงุฐูŠ ุฃู…ุฑุงุถ ุนูˆู†ุชู†ูŠ ููŠ ูƒุชุจุชู‡ุง ูˆุนูˆู†ูƒ ู…ูŠูƒูŠูˆููŠู„ูŠ ูˆุงู„ุฃู…ูŠุฑ ุนูˆู†ูƒ ูู„ุงู‚ุฑุงู
data/pending/text/raid-audio 1327.wav.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ู†ุนู… ูƒุงู†ุช ู‡ูŠ ูˆูƒุงู†ุช ุฑูˆุงูŠุฉ ู‚ุฑูŠุชู‡ุง ููŠ 1984
data/pending/text/raid-audio 1328.wav.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ู„ุง ู‡ุฐู‡ ุฑูˆุงูŠุฉ ุตูŠู†ูŠุฉ
data/pending/text/raid-audio 1329.wav.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ุฃุบุณ ุงู„ู…ุฌู†ูˆู†
data/pending/text/raid-audio 1330.wav.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ูˆุงู„ุฑูˆุงูŠุฉ ุจุฏุฃุช ู†ุนุฑู ุจู‡ุง ุชุฃู†ูŠ
data/pending/text/raid-audio 1331.wav.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ุงุณู…ู‡ุง ุฑูˆุงูŠุฉุŸ ุงู„ู‚ุณ ุงู„ู…ุฌู†ูˆู† ู‡ูŠ ุฑูˆุงูŠุฉ ุตูŠู†ูŠุฉ ุงู„ู‚ุณ ุงู„ู…ุฌู†ูˆู†ุŸ ู†ุนู… ู„ุง ุฃุนุฑูู‡ุง ูŠู‚ูˆู„ ู‡ุฐุง ู‡ูˆ ุฃุญุฏ ุงู„ุจุทู„ุงุช ูˆู‡ูˆ ุนู†ุฏู‡ ุฃุญุฏ ุงู„ู…ุงูŠู†ุณุช ูƒูŠู ุบูŠุฑ ู…ุซุงุจุฑุฉ ููŠ ุฑุฃุณู‡
data/pending/text/raid-audio 1332.wav.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ู†ู‚ูˆู„ ู„ูƒู… ู…ู‡ู… ู†ู†ุญูˆุณ
data/pending/text/raid-audio 1333.wav.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ู‡ูŠ ุตุญ ูƒุชู‚ูˆู„ ุงู„ูู„ุณูุฉ ุนุฏู…ูŠุฉ ุชู‚ุน
data/pending/text/raid-audio 1334.wav.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ู„ุง ู†ุฃุฎุฐ ู‡ุฐุง ุงู„ูƒูˆุชูŠ ู…ู† ุงู„ูƒูˆุชูŠ ุงู„ุฃุฎุฑ ูƒุฌุฑ ุงู„ู…ุซุงุจุฑ ูŠุฌูŠุฑูˆุด ูŠุจุง ูŠุญุฏ
data/pending/text/raid-audio 1335.wav.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ุญุชู‰ ูŠุฌุฏูˆุง ูƒู…ุง ู‚ู„ุช ู„ู‡ู… ู…ุฑุถุงุชูŠุด ุงู„ู†ุชูŠุฌุฉ
data/pending/text/raid-audio 1336.wav.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ุจูŠู‚ูˆู„ูƒ ุนุงุฏูŠ ู…ู‡ู… ู†ุจุฐู„ุช ุงู„ุฌู‡ุฉ
data/pending/text/raid-audio 1337.wav.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ุฎุงุทุฑ ููŠ ุจุงุช ุงู„ู„ู‚ุทุฉ ูŠู‚ูˆู„ ุญูˆุต ุบูŠุฑ ุนู„ู‰ ุงู„ููˆุงุฆุฏ ุงู„ุฃู‡ุฏุงู ุชุงุนู‡ ุดูŠู„ ูˆุณูŠู„ุฉ ูŠุงุฎุฏุฑ ูŠูˆุตู„ ุจู‡ุง ุงู„ุฃู‡ุฏุงู ู…ู‡ู… ู‡ู…ุง ุจุฐู„ ุฌู‡ุฉ ุชุงุนู‡
data/pending/text/raid-audio 1338.wav.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ู…ุง ุฃูˆุตู„ุด ู„ู‡ุฏูุชูŠ ูˆู…ุนู„ูŠุด
data/pending/text/raid-audio 1339.wav.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ูˆูƒูุงูŠุฉ ุฌูŠุฑูŠ ู…ุซู„ุง ุงู„ู†ุงุณ ุชุนุฑู ุงู„ู…ุงูŠู† ุณุช ูƒูุงูŠุฉ ุชูƒูˆู† ู‚ุงุฆุฏ
data/pending/text/raid-audio 1340.wav.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ุทุงุฑู‚ ููŠ ูู‡ุฉ
data/pending/text/raid-audio 1341.wav.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ุงุดุชุฑูƒูˆุง ููŠ ุงู„ู‚ู†ุงุฉ
data/pending/text/raid-audio 1342.wav.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ูˆุฑุงุก ู…ุง ุตู†ุน ุณูŠุทุฉ ุตู†ุน ุณูŠุทุฉ
data/pending/text/raid-audio 1343.wav.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ู„ุณุงู† ุฒูˆุชุง ูู† ุงู„ุญุฑุจ ู‡ุฐุง ู‡ูˆ ู‚ุฑูŠุช ู‚ุฑูŠุช ูˆุชุนู†ูŠ ู‚ุฑูŠุช ุชุจุงุฑูƒ ู‚ุฑูŠุช ูƒุชุจ ุงู„ู„ูŠ ู„ุงุฒู… ุชู‚ุฑุงู‡ู… ู„ุงุฒู… ูŠุฌูŠ ูŠุจุงู‡ุฑ ูู† ุงู„ุญุฑุจ
data/pending/text/raid-audio 1344.wav.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ุณู…ุนูˆู†ูƒ ุญุชู‰ ุนู†ุฏู‡ ูˆุงุญุฏุฉ ุงู„ุฅูŠุฏุงุก ุดุงุจุฉ ุนู„ู‰ ุงู„ุญุงู„ ูˆู‚ุงู„ ู„ูƒ ู…ู…ูƒู† ู†ุญู† ู†ุดูˆูู‡ุง ูˆุงุญุฏ ูŠู‚ุฑุฃ ูƒุชุงุจ ูŠู‚ูˆู„ ูƒุฐูŠ ุฃููƒุงุฑ ุนุงุฏูŠุฉ ู…ุง ููŠ ุงู„ูˆู‚ุช ุงู„ู„ูŠ ุฏุงุฑู‡ ู‚ุจู„ ุงู„ู…ูŠู„ุงุฏูŠุฉ ุฃููƒุงุฑ ูƒุงู†ุช ุทูุฑุฉ ูƒุงู†ุช
data/pending/text/raid-audio 1345.wav.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ู…ุซู„ุงู‹ ูƒู…ุง ูŠู‚ูˆู„ูˆู† ูˆุด ู‡ูŠ ุงู„ุจู„ุงุตุฉ ุงู„ุชูŠ ุชุญูƒู…ู‡ุง ูˆูƒูŠู ุชู‚ุฑุฃ ุงู„ุฃุนุฏุงุก ุฃุณูƒูˆ ุฌูŠูŠู†ูƒ ู‡ุงูƒ ูƒุชุงุจ ู…ููŠุฏ ุชุนุฑู ุชุฌุฑูŠ ุจู‡ุง ุณุงู†ุชุณูˆ ูู† ุงู„ุญุฑุจ ุฃู‡ู… ููƒุฑุฉ ูˆุด ู‡ูŠ ุชุงุน ุณุงู†ุชุณูˆ
data/pending/text/raid-audio 1346.wav.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ู„ุง ุฃู†ุณูŠุชูƒุŒ ู„ุง ูŠุฌุจ ุนู„ูŠูƒ ุฃู† ุชุชุฑูƒูŠุŒ ุจู„ ูŠู…ูƒู†ูƒ ุฃู† ุชุฑูƒุฒ ุนู„ู‰...
data/pending/text/raid-audio 1347.wav.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ู‡ูˆ ูŠุฑูƒุฒ ุนู„ู‰ ููƒุฑุฉ ุฃู†ู‡