diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 000000000..13566b81b
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/DollarBot.iml b/.idea/DollarBot.iml
new file mode 100644
index 000000000..6e7c09c28
--- /dev/null
+++ b/.idea/DollarBot.iml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 000000000..3205808ec
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 000000000..0ed127fcd
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 000000000..35eb1ddfb
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/code/code.py b/code/code.py
index 13b84473c..475833c2f 100644
--- a/code/code.py
+++ b/code/code.py
@@ -47,8 +47,12 @@
import monthly
import sendEmail
import add_recurring
+import os
+import tempfile
+import speech_recognition as sr
from datetime import datetime
from jproperties import Properties
+from pydub import AudioSegment
configs = Properties()
@@ -179,6 +183,59 @@ def command_weekly(message):
"""
weekly.run(message, bot)
+@bot.message_handler(content_types=['voice'])
+def handle_voice(message):
+ # Get the voice file
+ file_info = bot.get_file(message.voice.file_id)
+ downloaded_file = bot.download_file(file_info.file_path)
+
+ # Create a temporary OGG file
+ with tempfile.NamedTemporaryFile(delete=False, suffix='.ogg') as temp_ogg:
+ temp_ogg.write(downloaded_file)
+ temp_ogg_path = temp_ogg.name
+
+ # Convert OGG to WAV
+ temp_wav_path = tempfile.NamedTemporaryFile(delete=False, suffix='.wav').name
+ audio = AudioSegment.from_ogg(temp_ogg_path)
+ audio.export(temp_wav_path, format='wav')
+
+ # Use SpeechRecognition to convert voice to text
+ recognizer = sr.Recognizer()
+ with sr.AudioFile(temp_wav_path) as source:
+ audio_data = recognizer.record(source)
+ try:
+ text = recognizer.recognize_google(audio_data)
+ bot.send_message(message.chat.id, f"I heard: \"{text}\"")
+ process_command(text, message)
+ except sr.UnknownValueError:
+ bot.reply_to(message, "Sorry, I could not understand the audio.")
+ except sr.RequestError as e:
+ bot.reply_to(message, "Could not request results from the speech recognition service.")
+
+ # Cleanup: remove the temporary files
+ os.remove(temp_ogg_path)
+ os.remove(temp_wav_path)
+
+def process_command(text, message):
+ if "expense" in text:
+ command_add(message)
+ elif "history" in text:
+ command_history(message) # Call the existing history command
+ elif "budget" in text:
+ command_budget(message) # Call the existing budget command
+ elif "menu" in text:
+ start_and_menu_command(message)
+ elif "help" in text:
+ show_help(message)
+ elif "weekly" in text:
+ command_weekly(message)
+ elif "monthly" in text:
+ command_monthly(message)
+ elif "predict" in text:
+ command_predict(message)
+ else:
+ bot.send_message(message.chat.id, "I didn't recognize that command.")
+
# defines how the /monthly command has to be handled/processed
@bot.message_handler(commands=["monthly"])
def command_monthly(message):
diff --git a/history.csv b/history.csv
new file mode 100644
index 000000000..57398bb9c
--- /dev/null
+++ b/history.csv
@@ -0,0 +1,4 @@
+Date,Category,Amount
+02-Oct-2024,Food,$ 12
+12-Oct-2024,Groceries,$ 10.0
+26-Oct-2024,Food,$ 95.0
diff --git a/run.sh b/run.sh
old mode 100644
new mode 100755
diff --git a/setup.sh b/setup.sh
old mode 100644
new mode 100755
diff --git a/test/__init__.py b/test/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/test/test_voice.py b/test/test_voice.py
new file mode 100644
index 000000000..db0cb4fd6
--- /dev/null
+++ b/test/test_voice.py
@@ -0,0 +1,72 @@
+import unittest
+from unittest.mock import patch, MagicMock
+import tempfile
+import os
+from code import handle_voice, process_command
+
+class TestVoiceHandler(unittest.TestCase):
+
+ @patch('bot.get_file')
+ @patch('bot.download_file')
+ @patch('tempfile.NamedTemporaryFile')
+ @patch('pydub.AudioSegment.from_ogg')
+ @patch('speech_recognition.Recognizer')
+ def test_handle_voice_success(self, MockRecognizer, MockAudioSegment, MockNamedTempFile, MockDownloadFile, MockGetFile):
+ # Mock file details and download
+ MockGetFile.return_value.file_path = 'fake_path.ogg'
+ MockDownloadFile.return_value = b'fake_ogg_data'
+
+ # Mock tempfile behavior
+ temp_ogg = MagicMock()
+ temp_wav = MagicMock()
+ MockNamedTempFile.side_effect = [temp_ogg, temp_wav]
+ temp_ogg.name = 'temp.ogg'
+ temp_wav.name = 'temp.wav'
+
+ # Mock audio conversion
+ MockAudioSegment.from_ogg.return_value.export = MagicMock()
+
+ # Mock speech recognition
+ recognizer_instance = MockRecognizer.return_value
+ recognizer_instance.record.return_value = "fake_audio_data"
+ recognizer_instance.recognize_google.return_value = "this is a test expense"
+
+ # Mock process_command function
+ with patch('process_command') as mock_process_command:
+ handle_voice(MagicMock()) # Simulate calling the voice handler
+
+ # Assertions
+ MockGetFile.assert_called_once()
+ MockDownloadFile.assert_called_once()
+ MockAudioSegment.from_ogg.assert_called_once_with('temp.ogg')
+ recognizer_instance.recognize_google.assert_called_once()
+ mock_process_command.assert_called_once_with("this is a test expense", MagicMock())
+
+ # Cleanup mocks
+ os.remove('temp.ogg')
+ os.remove('temp.wav')
+
+ @patch('bot.send_message')
+ def test_process_command(self, mock_send_message):
+ message = MagicMock()
+
+ # Test different commands
+ with patch('command_add') as mock_command_add, \
+ patch('command_history') as mock_command_history, \
+ patch('command_budget') as mock_command_budget:
+
+ process_command("add expense", message)
+ mock_command_add.assert_called_once_with(message)
+
+ process_command("show history", message)
+ mock_command_history.assert_called_once_with(message)
+
+ process_command("set budget", message)
+ mock_command_budget.assert_called_once_with(message)
+
+ # Test unrecognized command
+ process_command("unknown command", message)
+ mock_send_message.assert_called_once_with(message.chat.id, "I didn't recognize that command.")
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/user_limits.json b/user_limits.json
new file mode 100644
index 000000000..6bd7dde21
--- /dev/null
+++ b/user_limits.json
@@ -0,0 +1,6 @@
+{
+ "6365998385": {
+ "food": 90.0,
+ "grocery": 70.0
+ }
+}
\ No newline at end of file