diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml new file mode 100644 index 0000000..7819196 --- /dev/null +++ b/.github/workflows/workflow.yml @@ -0,0 +1,31 @@ +name: CI + +on: + push: + branches: + - master # Replace with your main branch name if different + pull_request: + branches: + - master # This runs on pull requests targeting the main branch + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Set up Python + uses: actions/setup-python@v3 + with: + python-version: '3.12' # You can specify the version of Python to use + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + + - name: Run tests + run: | + pytest # Adjust this based on how you run your tests \ No newline at end of file diff --git a/AI.py b/AI.py deleted file mode 100644 index c80ac4b..0000000 --- a/AI.py +++ /dev/null @@ -1,47 +0,0 @@ -import requests - -class AIResponse: - def __init__(self, api_key): - self.api_key = api_key - - def get_ai_answer(self, question): - url = 'https://api.aimlapi.com/v1/chat/completions' - headers = { - 'Authorization': f'Bearer {self.api_key}', - 'Content-Type': 'application/json' - } - payload = { - 'text': question - } - - try: - response = requests.post(url, headers=headers, json=payload) - response.raise_for_status() # will raise an error for 4xx/5xx responses - result = response.json() - return result.get('answer', 'AI could not provide an answer.') - except requests.exceptions.HTTPError as http_err: - print(f"HTTP error occurred: {http_err}") - return 'AI API error: Unable to get a response.' - except Exception as err: - print(f"Other error occurred: {err}") - return 'AI API error: Unable to get a response.' - - - - - - - - - - - - - - - - - - # pydevcasts@gmail.com - # Poing1981@ - # https://meet.google.com/ikb-byyj-bui \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..19f681b --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2024 Siyamak Abbasnezhad, Mahan Shirsavar + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index bc65e4c..7027148 100644 --- a/README.md +++ b/README.md @@ -52,3 +52,8 @@ The Google Meet Bot is a Python application designed to automate participation i ## Conclusion This Google Meet Bot is a versatile tool for anyone looking to improve their online meeting experience by automating subtitle capture and translation. It is particularly useful for non-English speakers or those who wish to keep a record of meeting discussions. +## Select Options + +Made a Combo_Box By Pyqt6 +Made Modules For Each Langauge +Adding Each One, On By One To The Main File Where I Created Combo_Box By PyQt6 \ No newline at end of file diff --git a/auth.py b/auth.py deleted file mode 100644 index 015ee27..0000000 --- a/auth.py +++ /dev/null @@ -1,27 +0,0 @@ -# auth.py -import time -from selenium.webdriver.common.by import By -from selenium.webdriver.support.ui import WebDriverWait -from selenium.webdriver.support import expected_conditions as EC - -class Authenticator: - def __init__(self, driver, email, password): - self.driver = driver - self.email = email - self.password = password - - def login(self): - try: - self.driver.get('https://accounts.google.com/ServiceLogin') - self.driver.find_element(By.ID, "identifierId").send_keys(self.email) - self.driver.find_element(By.ID, "identifierNext").click() - - password_field = WebDriverWait(self.driver, 10).until( - EC.element_to_be_clickable((By.XPATH, '//*[@id="password"]/div[1]/div/div[1]/input'))) - password_field.send_keys(self.password) - - WebDriverWait(self.driver, 10).until(EC.url_contains("google.com")) - print("Login successful!") - except Exception as e: - print(f"Error during login: {e}") - self.driver.quit() diff --git a/chromedriver b/chromedriver deleted file mode 100755 index f498669..0000000 Binary files a/chromedriver and /dev/null differ diff --git a/controls.py b/controls.py deleted file mode 100644 index 0ea3526..0000000 --- a/controls.py +++ /dev/null @@ -1,22 +0,0 @@ -# controls.py -import time -from selenium.webdriver.common.by import By -from selenium.webdriver.support.ui import WebDriverWait -from selenium.webdriver.support import expected_conditions as EC -from selenium.common.exceptions import TimeoutException - -class MeetControls: - def __init__(self, driver): - self.driver = driver - - def turn_off_mic_cam(self): - try: - time.sleep(5) - mic_button = WebDriverWait(self.driver, 10).until( - EC.element_to_be_clickable((By.XPATH, '//*[contains(concat(" ", @class, " "), concat(" ", "crqnQb", " "))]'))) - mic_button.click() - print("Microphone turned off.") - except TimeoutException: - print("TimeoutException: Element not found or not clickable within specified time.") - except Exception as e: - print(f"An error occurred while turning off mic/cam: {e}") diff --git a/driver_setup.py b/driver_setup.py deleted file mode 100644 index 2cbf586..0000000 --- a/driver_setup.py +++ /dev/null @@ -1,21 +0,0 @@ -# driver_setup.py -from selenium import webdriver -from selenium.webdriver.chrome.service import Service -from selenium.webdriver.chrome.options import Options - -def setup_driver(): - options = Options() - options.add_argument('--disable-blink-features=AutomationControlled') - options.add_argument('--start-maximized') - options.add_experimental_option("prefs", { - "profile.default_content_setting_values.media_stream_mic": 1, - "profile.default_content_setting_values.media_stream_camera": 1, - "profile.default_content_setting_values.geolocation": 0, - "profile.default_content_setting_values.notifications": 1 - }) - - # You can also specify the path to your ChromeDriver if needed - # service = Service('/path/to/chromedriver') - # return webdriver.Chrome(service=service, options=options) - - return webdriver.Chrome(options=options) diff --git a/google-meet-icon.jpg b/google-meet-icon.jpg deleted file mode 100644 index 649b31d..0000000 Binary files a/google-meet-icon.jpg and /dev/null differ diff --git a/google_meet_bot.py b/google_meet_bot.py deleted file mode 100644 index 08972e4..0000000 --- a/google_meet_bot.py +++ /dev/null @@ -1,49 +0,0 @@ -# google_meet_bot.py -import time -import threading -from selenium.webdriver.common.by import By -from selenium.webdriver.support.ui import WebDriverWait -from selenium.webdriver.support import expected_conditions as EC -from selenium.common.exceptions import TimeoutException -from driver_setup import setup_driver -from translator import TextTranslator -from question_checker import QuestionChecker -from AI import AIResponse -from auth import Authenticator # Import the Authenticator class -from controls import MeetControls # Import the MeetControls class -from subtitle_saver import SubtitleSaver # Import the SubtitleSaver class - -class GoogleMeetBot: - def __init__(self, email, password, api_key, ai_api_key, update_signal): - self.email = email - self.password = password - self.api_key = api_key - self.ai_response = AIResponse(ai_api_key) - self.driver = setup_driver() - self.translator = TextTranslator() - self.question_checker = QuestionChecker(api_key) - self.authenticator = Authenticator(self.driver, self.email, self.password) - self.controls = MeetControls(self.driver) # Initialize MeetControls - self.subtitle_saver = SubtitleSaver(self.driver, self.translator, self.question_checker, update_signal) - - def login(self): - self.authenticator.login() # Use the Authenticator class for login - - def turn_off_mic_cam(self): - self.controls.turn_off_mic_cam() # Use the MeetControls class for turning off mic/cam - - def start(self, meeting_link): - self.login() - self.driver.get(meeting_link) - self.turn_off_mic_cam() - - subtitle_thread = threading.Thread(target=self.subtitle_saver.save_subtitles, daemon=True) # Use SubtitleSaver - subtitle_thread.start() - - try: - while True: - time.sleep(1) - except KeyboardInterrupt: - print("Program terminated by user.") - finally: - self.driver.quit() diff --git a/main.py b/main.py deleted file mode 100644 index f3ef657..0000000 --- a/main.py +++ /dev/null @@ -1,106 +0,0 @@ -import sys -import time -from PyQt6.QtWidgets import (QApplication, QWidget, QVBoxLayout, - QLineEdit, QPushButton, QTextEdit, QFileDialog) -from PyQt6.QtCore import QThread, pyqtSignal -import driver_setup -from google_meet_bot import GoogleMeetBot -from subtitle_saver import SubtitleSaver - -class MeetingThread(QThread): - update_output = pyqtSignal(str) - - def __init__(self, email, password, meeting_link, api_key, ai_api_key, translator, question_checker): - super().__init__() - self.email = email - self.password = password - self.meeting_link = meeting_link - self.api_key = api_key - self.ai_api_key = ai_api_key - self.driver = driver_setup - self.translator = translator - self.question_checker = question_checker - - def run(self): - self.update_output.emit("Starting the meeting...") - self.bot = GoogleMeetBot(self.email, self.password, self.api_key, self.ai_api_key, self.update_output) - self.bot.start(self.meeting_link) - - # Sending update_output as update_signal - subtitle_saver = SubtitleSaver(self.driver, self.translator, self.question_checker, self.update_output) - subtitle_saver.save_subtitles() # This method runs concurrently - -class MainWindow(QWidget): - def __init__(self): - super().__init__() - self.setWindowTitle("Google Meet Bot") - self.setGeometry(400, 400, 800, 800) - - # Assume you initialize translator and question_checker here - self.translator = None # Replace with actual translator - self.question_checker = None # Replace with actual question_checker - - layout = QVBoxLayout() - - self.email_input = QLineEdit(self) - self.email_input.setPlaceholderText("Enter your email") - self.email_input.setFixedHeight(40) # Increased height - layout.addWidget(self.email_input) - - self.password_input = QLineEdit(self) - self.password_input.setPlaceholderText("Enter your password") - self.password_input.setEchoMode(QLineEdit.EchoMode.Password) - self.password_input.setFixedHeight(40) # Increased height - layout.addWidget(self.password_input) - - self.meeting_link_input = QLineEdit(self) - self.meeting_link_input.setPlaceholderText("Enter meeting link") - self.meeting_link_input.setFixedHeight(40) # Increased height - layout.addWidget(self.meeting_link_input) - - self.start_button = QPushButton("Start Meeting", self) - self.start_button.setStyleSheet("background-color: blue; font-size: 16px;") # Change button color - self.start_button.setFixedHeight(40) # In - self.start_button.clicked.connect(self.start_meeting) - layout.addWidget(self.start_button) - - self.output_area = QTextEdit(self) - self.output_area.setReadOnly(True) - self.output_area.setFixedHeight(400) # Set a fixed height for output area - layout.addWidget(self.output_area) - - self.download_button = QPushButton("Download Text File", self) - self.download_button.setStyleSheet("background-color: green; font-size: 16px;") # Change button color - self.download_button.setFixedHeight(40) # In - self.download_button.clicked.connect(self.download_file) - layout.addWidget(self.download_button) - - self.setLayout(layout) - - def start_meeting(self): - email = self.email_input.text() - password = self.password_input.text() - meeting_link = self.meeting_link_input.text() - - if email and password and meeting_link: - self.meeting_thread = MeetingThread(email, password, meeting_link, 'YOUR_API_KEY', 'YOUR_AI_API_KEY', self.translator, self.question_checker) - self.meeting_thread.update_output.connect(self.append_output) - self.meeting_thread.start() - else: - self.output_area.append("Please fill in all fields.") - - def append_output(self, message): - self.output_area.append(message) - - def download_file(self): - file_name, _ = QFileDialog.getSaveFileName(self, "Save File", "", "Text Files (*.txt);;All Files (*)") - if file_name: - with open(file_name, 'w', encoding='utf-8') as file: - file.write(self.output_area.toPlainText()) - self.output_area.append(f"File saved at {file_name}.") - -if __name__ == "__main__": - app = QApplication(sys.argv) - window = MainWindow() - window.show() - sys.exit(app.exec()) diff --git a/question_checker.py b/question_checker.py deleted file mode 100644 index fede79c..0000000 --- a/question_checker.py +++ /dev/null @@ -1,32 +0,0 @@ -# question_checker.py -import requests - -class QuestionChecker: - def __init__(self, api_key): - self.api_key = api_key - - def is_question(self, text): - wh_question_detected = self.contains_wh_question(text) - if wh_question_detected: - return True - - url = 'https://api.aimlapi.com/v1/chat/completions' # Replace with your API endpoint - headers = { - 'Authorization': f'Bearer {self.api_key}', - 'Content-Type': 'application/json' - } - payload = { - 'text': text - } - response = requests.post(url, headers=headers, json=payload) - if response.status_code == 200: - result = response.json() - return result.get('is_question', False) - else: - print(f"API error: {response.status_code}, Message: {response.text}") - return False - - def contains_wh_question(self, text): - wh_words = ['who', 'what', 'where', 'when', 'why', 'which'] - text_lower = text.lower() - return any(wh in text_lower for wh in wh_words) diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..7e90440 --- /dev/null +++ b/setup.py @@ -0,0 +1,23 @@ +from setuptools import setup, find_packages + +setup( + name='MeetMa', # Package name in lowercase + version='0.0.1', + packages=find_packages(where='.'), # Automatically find packages + install_requires=[ + 'PyQt6', + 'beautifulsoup4', + 'googletrans', + 'requests', + 'selenium' + ], + entry_points={ + 'console_scripts': [ + 'meetma=src.main:main', # Adjust based on your actual structure + ], + }, + author='Siyamak Abasnezhad, Mahan Shirsavar', + author_email='pydevcasts@gmail.com', + description='Google Meet Bot is a Python application that automates participation in Google Meet meetings by extracting real-time subtitles, translating them, and detecting questions, enhancing users online meeting experience.', + url='https://github.com/pydevcasts/MeetMa', # Your GitHub URL +) \ No newline at end of file diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000..cfa541f --- /dev/null +++ b/src/__init__.py @@ -0,0 +1 @@ +from main import MainWindow \ No newline at end of file diff --git a/src/google_meet_bot.py b/src/google_meet_bot.py index 08972e4..e7501e2 100644 --- a/src/google_meet_bot.py +++ b/src/google_meet_bot.py @@ -9,12 +9,14 @@ from translator import TextTranslator from question_checker import QuestionChecker from AI import AIResponse -from auth import Authenticator # Import the Authenticator class -from controls import MeetControls # Import the MeetControls class -from subtitle_saver import SubtitleSaver # Import the SubtitleSaver class +from auth import Authenticator +from controls import MeetControls +from subtitle_saver_fa import SubtitleSaverFa +from subtitle_saver_fr import SubtitleSaverFr +from subtitle_saver_ch import SubtitleSaverCh class GoogleMeetBot: - def __init__(self, email, password, api_key, ai_api_key, update_signal): + def __init__(self, email, password, api_key, ai_api_key, update_signal, selected_language): self.email = email self.password = password self.api_key = api_key @@ -23,27 +25,42 @@ def __init__(self, email, password, api_key, ai_api_key, update_signal): self.translator = TextTranslator() self.question_checker = QuestionChecker(api_key) self.authenticator = Authenticator(self.driver, self.email, self.password) - self.controls = MeetControls(self.driver) # Initialize MeetControls - self.subtitle_saver = SubtitleSaver(self.driver, self.translator, self.question_checker, update_signal) - + self.controls = MeetControls(self.driver) + + # Initialize the subtitle saver based on the selected language + if selected_language == "Farsi": + self.subtitle_saver = SubtitleSaverFa(self.driver, self.translator, self.question_checker, update_signal) + elif selected_language == "French": + self.subtitle_saver = SubtitleSaverFr(self.driver, self.translator, self.question_checker, update_signal) + elif selected_language == "Chinese": + self.subtitle_saver = SubtitleSaverCh(self.driver, self.translator, self.question_checker, update_signal) + def login(self): - self.authenticator.login() # Use the Authenticator class for login + """Log in to Google Meet using the Authenticator class.""" + self.authenticator.login() def turn_off_mic_cam(self): - self.controls.turn_off_mic_cam() # Use the MeetControls class for turning off mic/cam + """Turn off microphone and camera using the MeetControls class.""" + self.controls.turn_off_mic_cam() def start(self, meeting_link): + """Start the Google Meet session.""" self.login() self.driver.get(meeting_link) self.turn_off_mic_cam() - subtitle_thread = threading.Thread(target=self.subtitle_saver.save_subtitles, daemon=True) # Use SubtitleSaver + # Start the subtitle saver in a separate thread + subtitle_thread = threading.Thread(target=self.subtitle_saver.save_subtitles, daemon=True) subtitle_thread.start() try: - while True: - time.sleep(1) + self._keep_running() except KeyboardInterrupt: print("Program terminated by user.") finally: self.driver.quit() + + def _keep_running(self): + """Keep the bot running until interrupted.""" + while True: + time.sleep(1) diff --git a/src/main.py b/src/main.py index f3ef657..4b498cd 100644 --- a/src/main.py +++ b/src/main.py @@ -1,34 +1,53 @@ + + import sys -import time from PyQt6.QtWidgets import (QApplication, QWidget, QVBoxLayout, - QLineEdit, QPushButton, QTextEdit, QFileDialog) + QLineEdit, QPushButton, QTextEdit, QFileDialog, QComboBox) from PyQt6.QtCore import QThread, pyqtSignal import driver_setup from google_meet_bot import GoogleMeetBot -from subtitle_saver import SubtitleSaver - +from subtitle_saver_fr import SubtitleSaverFr +from subtitle_saver_fa import SubtitleSaverFa +from subtitle_saver_ch import SubtitleSaverCh class MeetingThread(QThread): update_output = pyqtSignal(str) - def __init__(self, email, password, meeting_link, api_key, ai_api_key, translator, question_checker): + def __init__(self, email, password, meeting_link, api_key, ai_api_key, translator, question_checker, selected_language): super().__init__() self.email = email self.password = password self.meeting_link = meeting_link self.api_key = api_key self.ai_api_key = ai_api_key - self.driver = driver_setup self.translator = translator self.question_checker = question_checker + self.selected_language = selected_language def run(self): self.update_output.emit("Starting the meeting...") - self.bot = GoogleMeetBot(self.email, self.password, self.api_key, self.ai_api_key, self.update_output) + self.bot = GoogleMeetBot( + self.email, + self.password, + self.api_key, + self.ai_api_key, + self.update_output, + self.selected_language + ) self.bot.start(self.meeting_link) - # Sending update_output as update_signal - subtitle_saver = SubtitleSaver(self.driver, self.translator, self.question_checker, self.update_output) - subtitle_saver.save_subtitles() # This method runs concurrently + # Subtitle saving based on selected language + + + subtitle_saver = None + if self.selected_language == "Farsi": + subtitle_saver = SubtitleSaverFa(self.driver, self.translator, self.question_checker, self.update_output) + elif self.selected_language == "French": + subtitle_saver = SubtitleSaverFr(self.driver, self.translator, self.question_checker, self.update_output) + elif self.selected_language == "Chinese": + subtitle_saver = SubtitleSaverCh(self.driver, self.translator, self.question_checker, self.update_output) + + if subtitle_saver: + subtitle_saver.save_subtitles() class MainWindow(QWidget): def __init__(self): @@ -36,7 +55,7 @@ def __init__(self): self.setWindowTitle("Google Meet Bot") self.setGeometry(400, 400, 800, 800) - # Assume you initialize translator and question_checker here + # Initialize translator and question_checker here self.translator = None # Replace with actual translator self.question_checker = None # Replace with actual question_checker @@ -44,34 +63,39 @@ def __init__(self): self.email_input = QLineEdit(self) self.email_input.setPlaceholderText("Enter your email") - self.email_input.setFixedHeight(40) # Increased height + self.email_input.setFixedHeight(40) layout.addWidget(self.email_input) self.password_input = QLineEdit(self) self.password_input.setPlaceholderText("Enter your password") self.password_input.setEchoMode(QLineEdit.EchoMode.Password) - self.password_input.setFixedHeight(40) # Increased height + self.password_input.setFixedHeight(40) layout.addWidget(self.password_input) self.meeting_link_input = QLineEdit(self) self.meeting_link_input.setPlaceholderText("Enter meeting link") - self.meeting_link_input.setFixedHeight(40) # Increased height + self.meeting_link_input.setFixedHeight(40) layout.addWidget(self.meeting_link_input) + # Create a QComboBox for selecting subtitle language + self.language_combo = QComboBox(self) + self.language_combo.addItems(["Select Language", "Farsi","French", "Chinese"]) + layout.addWidget(self.language_combo) + self.start_button = QPushButton("Start Meeting", self) - self.start_button.setStyleSheet("background-color: blue; font-size: 16px;") # Change button color - self.start_button.setFixedHeight(40) # In + self.start_button.setStyleSheet("background-color: blue; font-size: 16px;") + self.start_button.setFixedHeight(40) self.start_button.clicked.connect(self.start_meeting) layout.addWidget(self.start_button) self.output_area = QTextEdit(self) self.output_area.setReadOnly(True) - self.output_area.setFixedHeight(400) # Set a fixed height for output area + self.output_area.setFixedHeight(400) layout.addWidget(self.output_area) self.download_button = QPushButton("Download Text File", self) - self.download_button.setStyleSheet("background-color: green; font-size: 16px;") # Change button color - self.download_button.setFixedHeight(40) # In + self.download_button.setStyleSheet("background-color: green; font-size: 16px;") + self.download_button.setFixedHeight(40) self.download_button.clicked.connect(self.download_file) layout.addWidget(self.download_button) @@ -81,13 +105,24 @@ def start_meeting(self): email = self.email_input.text() password = self.password_input.text() meeting_link = self.meeting_link_input.text() - - if email and password and meeting_link: - self.meeting_thread = MeetingThread(email, password, meeting_link, 'YOUR_API_KEY', 'YOUR_AI_API_KEY', self.translator, self.question_checker) + selected_language = self.language_combo.currentText() + + if email and password and meeting_link and selected_language != "Select Language": + self.meeting_thread = MeetingThread( + email, + password, + meeting_link, + 'YOUR_API_KEY', + 'YOUR_AI_API_KEY', + self.translator, + self.question_checker, + selected_language # Send the selected language here + ) self.meeting_thread.update_output.connect(self.append_output) self.meeting_thread.start() else: - self.output_area.append("Please fill in all fields.") + self.output_area.append("Please fill in all fields and select a language.") + def append_output(self, message): self.output_area.append(message) @@ -103,4 +138,4 @@ def download_file(self): app = QApplication(sys.argv) window = MainWindow() window.show() - sys.exit(app.exec()) + sys.exit(app.exec()) \ No newline at end of file diff --git a/src/subtitle_saver.py b/src/subtitle_saver.py index 04fd70a..9f83e8f 100644 --- a/src/subtitle_saver.py +++ b/src/subtitle_saver.py @@ -32,8 +32,9 @@ def save_subtitles(self): translated_text = self.translator.translate(subtitle_text, dest='fa') f.write("Persian: " + translated_text + '\n' + "*" * 20 + '\n') + f.flush() - self.update_signal.emit(f"English: {subtitle_text}\nType: {'Question' if self.question_checker.is_question(subtitle_text) else 'Statement'}\nPersian: {translated_text}") + self.update_signal.emit(f"English: {subtitle_text}\nType: {'Question' if self.question_checker.is_question(subtitle_text) else 'Statement'}\nPersian: {translated_text}\n\n French: {translated_text}") self.previous_subtitles.add(subtitle_text) time.sleep(1) @@ -41,4 +42,4 @@ def save_subtitles(self): print("TimeoutException: Element not found.") except Exception as e: print(f"Error: {e}") - break + break \ No newline at end of file diff --git a/src/subtitle_saver_ch.py b/src/subtitle_saver_ch.py new file mode 100644 index 0000000..47e58c5 --- /dev/null +++ b/src/subtitle_saver_ch.py @@ -0,0 +1,54 @@ +# subtitle_saver.py +import time +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC +from selenium.common.exceptions import TimeoutException +from translator import TextTranslator + +class SubtitleSaverCh: + def __init__(self, driver, translator,question_checker, update_signal): + self.driver = driver + self.translator = translator + self.question_checker = question_checker + self.previous_subtitles = set() + self.update_signal = update_signal + + def save_subtitles(self): + with open('subtitles.txt', 'a', encoding='utf-8') as f: + while True: + try: + subtitle_text = self._get_subtitle_text() + if self._is_new_subtitle(subtitle_text): + self._write_subtitle_to_file(f, subtitle_text) + self.previous_subtitles.add(subtitle_text) + time.sleep(1) + except TimeoutException: + print("TimeoutException: Element not found.") + except Exception as e: + print(f"Error: {e}") + break + + def _get_subtitle_text(self): + """Retrieve subtitle text from the web element.""" + subtitles = WebDriverWait(self.driver, 10).until( + EC.presence_of_element_located((By.XPATH, '//*[contains(concat(" ", @class, " "), concat(" ", "iOzk7", " "))]'))) + + return subtitles.text.strip() + + def _is_new_subtitle(self, subtitle_text): + """Check if the subtitle is new and not previously recorded.""" + return subtitle_text and subtitle_text not in self.previous_subtitles + + def _write_subtitle_to_file(self, f, subtitle_text): + """Write the subtitle and its translation to the file.""" + f.write("English: " + subtitle_text + '\n' + "*" * 20 + '\n') + + subtitle_type = "Question" if self.question_checker.is_question(subtitle_text) else "Statement" + f.write(f"Type: {subtitle_type}\n" + "*" * 20 + '\n') + + translated_text_ch = self.translator.translate(subtitle_text, dest='zh-TW') + f.write("Chinese: " + translated_text_ch + '\n' + "*" * 20 + '\n') + f.flush() + + self.update_signal.emit(f"English: {subtitle_text}\nType: {subtitle_type}\nChinese: {translated_text_ch}\n\n") diff --git a/src/subtitle_saver_fa.py b/src/subtitle_saver_fa.py new file mode 100644 index 0000000..a4a95af --- /dev/null +++ b/src/subtitle_saver_fa.py @@ -0,0 +1,54 @@ +# subtitle_saver.py +import time +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC +from selenium.common.exceptions import TimeoutException +from translator import TextTranslator + +class SubtitleSaverFa: + def __init__(self, driver, translator,question_checker, update_signal): + self.driver = driver + self.translator = translator + self.question_checker = question_checker + self.previous_subtitles = set() + self.update_signal = update_signal + + def save_subtitles(self): + with open('subtitles.txt', 'a', encoding='utf-8') as f: + while True: + try: + subtitle_text = self._get_subtitle_text() + if self._is_new_subtitle(subtitle_text): + self._write_subtitle_to_file(f, subtitle_text) + self.previous_subtitles.add(subtitle_text) + time.sleep(1) + except TimeoutException: + print("TimeoutException: Element not found.") + except Exception as e: + print(f"Error: {e}") + break + + def _get_subtitle_text(self): + """Retrieve subtitle text from the web element.""" + subtitles = WebDriverWait(self.driver, 10).until( + EC.presence_of_element_located((By.XPATH, '//*[contains(concat(" ", @class, " "), concat(" ", "iOzk7", " "))]'))) + return subtitles.text.strip() + + def _is_new_subtitle(self, subtitle_text): + """Check if the subtitle is new and not previously recorded.""" + return subtitle_text and subtitle_text not in self.previous_subtitles + + def _write_subtitle_to_file(self, f, subtitle_text): + """Write the subtitle and its translation to the file.""" + f.write("English: " + subtitle_text + '\n' + "*" * 20 + '\n') + + subtitle_type = "Question" if self.question_checker.is_question(subtitle_text) else "Statement" + f.write(f"Type: {subtitle_type}\n" + "*" * 20 + '\n') + + translated_text = self.translator.translate(subtitle_text, dest='fa') + f.write("Persian: " + translated_text + '\n' + "*" * 20 + '\n') + f.flush() + + self.update_signal.emit(f"English: {subtitle_text}\nType: {subtitle_type}\nPersian: {translated_text}\n\n") + diff --git a/src/subtitle_saver_fr.py b/src/subtitle_saver_fr.py new file mode 100644 index 0000000..5c612ed --- /dev/null +++ b/src/subtitle_saver_fr.py @@ -0,0 +1,55 @@ +# subtitle_saver.py +import time +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC +from selenium.common.exceptions import TimeoutException +from translator import TextTranslator + + +class SubtitleSaverFr: + def __init__(self, driver, translator,question_checker, update_signal): + self.driver = driver + self.translator = translator + self.question_checker = question_checker + self.previous_subtitles = set() + self.update_signal = update_signal + + def save_subtitles(self): + with open('subtitles.txt', 'a', encoding='utf-8') as f: + while True: + try: + subtitle_text = self._get_subtitle_text() + if self._is_new_subtitle(subtitle_text): + self._write_subtitle_to_file(f, subtitle_text) + self.previous_subtitles.add(subtitle_text) + time.sleep(1) + except TimeoutException: + print("TimeoutException: Element not found.") + except Exception as e: + print(f"Error: {e}") + break + + def _get_subtitle_text(self): + """Retrieve subtitle text from the web element.""" + subtitles = WebDriverWait(self.driver, 10).until( + EC.presence_of_element_located((By.XPATH, '//*[contains(concat(" ", @class, " "), concat(" ", "iOzk7", " "))]'))) + return subtitles.text.strip() + + def _is_new_subtitle(self, subtitle_text): + """Check if the subtitle is new and not previously recorded.""" + return subtitle_text and subtitle_text not in self.previous_subtitles + + def _write_subtitle_to_file(self, f, subtitle_text): + """Write the subtitle and its translation to the file.""" + f.write("English: " + subtitle_text + '\n' + "*" * 20 + '\n') + + subtitle_type = "Question" if self.question_checker.is_question(subtitle_text) else "Statement" + f.write(f"Type: {subtitle_type}\n" + "*" * 20 + '\n') + + translated_text = self.translator.translate(subtitle_text, dest='fr') + f.write("French: " + translated_text + '\n' + "*" * 20 + '\n') + f.flush() + + self.update_signal.emit(f"English: {subtitle_text}\nType: {subtitle_type}\nFrench: {translated_text}\n") + diff --git a/src/subtitles.txt b/src/subtitles.txt new file mode 100644 index 0000000..030cb22 --- /dev/null +++ b/src/subtitles.txt @@ -0,0 +1,32 @@ +English: You +Hello. +******************** +Type: Statement +******************** +Chinese: 你 +你好。 +******************** +English: siyamak abasnezhad +Hello. Or are you? +You +What are you? Good. +******************** +Type: Question +******************** +Chinese: Siyamak Abasnezhad +你好。還是你? +你 +你是做什麼的?好的。 +******************** +English: You +What are you? Good luck. +siyamak abasnezhad +Good luck. +******************** +Type: Question +******************** +Chinese: 你 +你是做什麼的?祝你好運。 +Siyamak Abasnezhad +祝你好運。 +******************** diff --git a/src/translator.py b/src/translator.py index f67b804..2a00fc3 100644 --- a/src/translator.py +++ b/src/translator.py @@ -6,4 +6,16 @@ def __init__(self): self.translator = Translator() def translate(self, text, dest='fa'): + """Translate text to the specified destination language.""" return self.translator.translate(text, dest=dest).text + + def translate_to_french(self, text): + """Translate text to French.""" + return self.translate(text, dest='fr') + + def translate_to_chinese(self, text): + """Translate text to Chinese (Traditional).""" + return self.translate(text, dest='zh-TW') + def is_question(self, text): + # Simple check to see if the text is a question + return text.strip().endswith('?') \ No newline at end of file diff --git a/subtitle_saver.py b/subtitle_saver.py deleted file mode 100644 index 04fd70a..0000000 --- a/subtitle_saver.py +++ /dev/null @@ -1,44 +0,0 @@ -# subtitle_saver.py -import time -from selenium.webdriver.common.by import By -from selenium.webdriver.support.ui import WebDriverWait -from selenium.webdriver.support import expected_conditions as EC -from selenium.common.exceptions import TimeoutException - -class SubtitleSaver: - def __init__(self, driver, translator, question_checker, update_signal): - self.driver = driver - self.translator = translator - self.question_checker = question_checker - self.previous_subtitles = set() - self.update_signal = update_signal - - def save_subtitles(self): - with open('subtitles.txt', 'a', encoding='utf-8') as f: - while True: - try: - subtitles = WebDriverWait(self.driver, 10).until( - EC.presence_of_element_located((By.XPATH, '//*[contains(concat(" ", @class, " "), concat(" ", "iOzk7", " "))]'))) - subtitle_text = subtitles.text.strip() - - if subtitle_text and subtitle_text not in self.previous_subtitles: - f.write("English: " + subtitle_text + '\n' + "*" * 20 + '\n') - - if self.question_checker.is_question(subtitle_text): - f.write("Type: Question" + '\n' + "*" * 20 + '\n') - else: - f.write("Type: Statement" + '\n' + "*" * 20 + '\n') - - translated_text = self.translator.translate(subtitle_text, dest='fa') - f.write("Persian: " + translated_text + '\n' + "*" * 20 + '\n') - - f.flush() - self.update_signal.emit(f"English: {subtitle_text}\nType: {'Question' if self.question_checker.is_question(subtitle_text) else 'Statement'}\nPersian: {translated_text}") - - self.previous_subtitles.add(subtitle_text) - time.sleep(1) - except TimeoutException: - print("TimeoutException: Element not found.") - except Exception as e: - print(f"Error: {e}") - break diff --git a/subtitles.txt b/subtitles.txt new file mode 100644 index 0000000..9dc1a39 --- /dev/null +++ b/subtitles.txt @@ -0,0 +1,32 @@ +English: siyamak abasnezhad +Hello. +******************** +Type: Statement +******************** +Persian: سیهاماک آبرس +سلام +******************** +English: siyamak abasnezhad +Iron Man. +You +I +******************** +Type: Statement +******************** +Persian: سیهاماک آبرس +مرد آهنین +شما +من +******************** +English: You +I What? +siyamak abasnezhad +so, you doing +******************** +Type: Question +******************** +Persian: شما +من چی؟ +سیهاماک آبرس +بنابراین ، شما انجام می دهید +******************** diff --git a/test.py b/test.py deleted file mode 100644 index f81ac8a..0000000 --- a/test.py +++ /dev/null @@ -1,68 +0,0 @@ -import unittest -from unittest.mock import patch, MagicMock -from google_meet_bot import GoogleMeetBot - -class TestGoogleMeetBot(unittest.TestCase): - - @patch('google_meet_bot.setup_driver') - @patch('google_meet_bot.Authenticator') - @patch('google_meet_bot.MeetControls') - @patch('google_meet_bot.SubtitleSaver') - @patch('google_meet_bot.TextTranslator') - @patch('google_meet_bot.QuestionChecker') - @patch('google_meet_bot.AIResponse') - def setUp(self, mock_ai_response, mock_question_checker, mock_text_translator, - mock_subtitle_saver, mock_meet_controls, mock_authenticator, - mock_setup_driver): - # Mocking the setup for GoogleMeetBot - self.mock_driver = MagicMock() - mock_setup_driver.return_value = self.mock_driver - self.bot = GoogleMeetBot( - email='test@example.com', - password='password123', - api_key='fake_api_key', - ai_api_key='fake_ai_api_key', - update_signal=MagicMock() - ) - self.bot.authenticator = mock_authenticator.return_value - self.bot.controls = mock_meet_controls.return_value - self.bot.subtitle_saver = mock_subtitle_saver.return_value - self.bot.translator = mock_text_translator.return_value - self.bot.question_checker = mock_question_checker.return_value - self.bot.ai_response = mock_ai_response.return_value - - def test_login(self): - # Test that the login method calls the Authenticator's login method - self.bot.login() - self.bot.authenticator.login.assert_called_once() - - def test_turn_off_mic_cam(self): - # Test that the turn_off_mic_cam method calls the MeetControls method - self.bot.turn_off_mic_cam() - self.bot.controls.turn_off_mic_cam.assert_called_once() - - @patch('google_meet_bot.time.sleep', return_value=None) # Mock sleep to avoid delays - def test_start_meeting(self, mock_sleep): - meeting_link = "https://meet.google.com/test-meeting" - self.bot.start(meeting_link) - - # Check that the login method is called - self.bot.authenticator.login.assert_called_once() - - # Check that the driver navigates to the meeting link - self.bot.driver.get.assert_called_with(meeting_link) - - # Check that the microphone and camera are turned off - self.bot.controls.turn_off_mic_cam.assert_called_once() - - # Check that the subtitle saver thread is started - self.assertTrue(self.bot.subtitle_saver.save_subtitles.called) - - def test_program_termination(self): - # Test that the driver quits when the program is terminated - with self.assertRaises(KeyboardInterrupt): - self.bot.start("https://meet.google.com/test-meeting") - self.bot.driver.quit.assert_called_once() - -if __name__ == '__main__': - unittest.main() \ No newline at end of file diff --git a/translator.py b/translator.py deleted file mode 100644 index f67b804..0000000 --- a/translator.py +++ /dev/null @@ -1,9 +0,0 @@ -# translator.py -from googletrans import Translator - -class TextTranslator: - def __init__(self): - self.translator = Translator() - - def translate(self, text, dest='fa'): - return self.translator.translate(text, dest=dest).text