From 8bf3ee828ea3c0567619c648b38a7c3d1bb57f96 Mon Sep 17 00:00:00 2001 From: Profitroll Date: Sun, 27 Aug 2023 23:47:07 +0300 Subject: [PATCH 01/36] Added translation using Weblate (German) --- locale/de.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 locale/de.json diff --git a/locale/de.json b/locale/de.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/locale/de.json @@ -0,0 +1 @@ +{} From 9f17b120fad1ddeead1573781bd8f06a1e6eb815 Mon Sep 17 00:00:00 2001 From: Profitroll Date: Sun, 27 Aug 2023 23:47:32 +0300 Subject: [PATCH 02/36] Added translation using Weblate (Russian) --- locale/ru.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 locale/ru.json diff --git a/locale/ru.json b/locale/ru.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/locale/ru.json @@ -0,0 +1 @@ +{} From 25a6ebef48640a99375af3e7baa38b9bf61a94f6 Mon Sep 17 00:00:00 2001 From: Profitroll Date: Sun, 27 Aug 2023 23:53:22 +0300 Subject: [PATCH 03/36] Deleted translation using Weblate (Russian) --- locale/ru.json | 1 - 1 file changed, 1 deletion(-) delete mode 100644 locale/ru.json diff --git a/locale/ru.json b/locale/ru.json deleted file mode 100644 index 0967ef4..0000000 --- a/locale/ru.json +++ /dev/null @@ -1 +0,0 @@ -{} From e77cfea55392cd409974008af115e8ab595946c2 Mon Sep 17 00:00:00 2001 From: Profitroll Date: Wed, 30 Aug 2023 09:19:07 +0000 Subject: [PATCH 04/36] Translated using Weblate (Ukrainian) Currently translated at 100.0% (78 of 78 strings) Translation: GarbageReminder/TelegramBot Translate-URL: https://weblate.end-play.xyz/projects/garbagereminder/telegrambot/uk/ --- locale/uk.json | 74 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 63 insertions(+), 11 deletions(-) diff --git a/locale/uk.json b/locale/uk.json index 718bed6..b6f78f7 100644 --- a/locale/uk.json +++ b/locale/uk.json @@ -21,27 +21,79 @@ }, "commands": { "help": "Показати меню допомоги", - "setup": "Обрати місто та район", - "toggle": "Вимкнути/вимкнути повідомлення", - "set_time": "Встановити час повідомлень", - "set_offset": "Встановити дні випередження", - "upcoming": "Дати збирання на наступні 30 днів", + "setup": "Обрати місце розташування", + "toggle": "Увімкнути/вимкнути сповіщення", + "set_time": "Встановити час сповіщень", + "set_offset": "Встановити зміщення сповіщень", + "upcoming": "Дати збору на наступні 30 днів", "language": "Змінити мову повідомлень бота", - "checkout": "Експортувати чи видалити дані", + "checkout": "Експортувати або видалити дані", "import": "Завантажити з JSON до бази даних", "shutdown": "Вимкнути бота", "remove_commands": "Видалити всі команди" }, "messages": { - "help": "Привіт! Я твій бот!", + "help": "🔔 Цей бот надсилає сповіщення про вивезення сміття згідно з Вашим місцевим графіком.\n\n**Доступні команди**\n/help - Показати це повідомлення\n/setup - Вибрати місце розташування\n/toggle - Увімкнути/вимкнути нагадування\n/set_time - Встановити час нагадувань\n/set_offset - Встановити зсув між нагадуваннями та вивозом\n/upcoming - Показати майбутні вивезення\n/language - Обрати мову бота\n/checkout - Експортувати або видалити дані\n\nВи також можете запропонувати додати своє місто/район до бота, зв'язавшись з адміністраторами за [цим посиланням]({url_contact}) та вказавши свій розклад.\n\n⚙️ Бажаєте розмістити цього бота самостійно або внести деякі зміни? Бот має відкритий вихідний код, тож ви можете форкнути його. Ознайомтесь із [репозиторієм бота]({url_repo}) щоб дізнатись деталі.\n\nПриємного користування! 🤗", "start": "👋 Вітання!\n\nЦей невеличкий бот з відкритим вихідним кодом створений для того, щоб трохи спростити Вам життя, надсилаючи сповіщення про вивіз сміття у вашому регіоні.\n\nКористуючись цим ботом, Ви приймаєте [Політику конфіденційності]({privacy_policy}), в іншому випадку, будь ласка, заблокуйте та видаліть цього бота перед подальшою взаємодією.\n\nТепер офіційна частина закінчена, тож Ви можете зануритися в бота.", - "locale_choice": "Гаразд. Будь ласка, оберіть мову за допомогою клавіатури нижче." + "locale_choice": "Гаразд. Будь ласка, оберіть мову за допомогою клавіатури нижче.", + "set_time": "Гаразд. Будь ласка, надішліть бажаний час у форматі ГГ:ХХ.", + "cancel": "Якщо Ви хочете скасувати цю операцію, використовуйте /cancel.", + "start_code_invalid": "🚫 Ви запустили бота за посиланням, що містить локацію, але, схоже, вона не є дійсною. Будь ласка, скористайтеся командою /setup, щоб налаштувати локацію вручну.", + "checkout_deleted": "🗑️ Ваші дані було видалено. Якщо Ви хочете знову почати користуватися цим ботом, скористайтеся командою /setup. В іншому випадку видаліть/заблокуйте бота і більше не взаємодійте з ним.", + "setup_finished": "✅ Готово! Ваше місцезнаходження тепер **{ім'я}**. Ви будете отримувати сповіщення про вивезення сміття за {offset} д. заздалегідь о {time}.", + "checkout_deletion": "Гаразд. Будь ласка, підтвердіть, що Ви хочете видалити свої дані з бота.\n\nНаступні дані будуть видалені:\n• Вибране місце\n• Бажана мова всіх повідомлень\n• Час сповіщень\n• Зсув сповіщень\n\nВикористовуйте клавіатуру, щоб підтвердити й продовжити або /cancel, щоб перервати цю операцію.", + "setup": "⚙️ Почнемо налаштування з пошуку Вашого місцезнаходження.\n\nБудь ласка, виберіть, чи хочете Ви шукати серед найближчих до вас локацій, чи одразу перейти до пошуку за назвою.\n\nЗверніть увагу, що надіслане вами місцезнаходження **НЕ** зберігається ніде і використовується лише для пошуку місць поряд в базі даних.", + "start_selection_no": "Гаразд, тепер Ви самі по собі. Будь ласка, скористайтеся командою /setup, щоб налаштувати своє місцезнаходження і почати отримувати нагадування.", + "start_selection_yes": "✅ Готово! Ваша локація тепер **{name}**. Ви будете отримувати нагадування про вивезення сміття за {offset} д. заздалегідь о {time}.\n\nБудь ласка, скористайтесь /help, якщо Ви хочете дізнатися, як змінити час сповіщень або вимкнути їх.", + "upcoming_empty": "Не знайдено записів про вивезення сміття на найближчі 30 днів для **{name}**", + "location_name": "Будь ласка, надішліть мені назву населеного пункту. Це має бути назва, яка використовується у графіку вивезення сміття Вашою місцевою владою. Зазвичай це назва району або міста.", + "set_offset_finished": "🔔 Час сповіщень було оновлено! Тепер Ви будете отримувати сповіщення про вивіз сміття за **{offset} д.** до вивозу о {time}. {toggle_notice}", + "set_time_finished": "🔔 Час сповіщень було оновлено! Тепер Ви будете отримувати сповіщення про вивіз сміття за {offset} д. до вивозу о **{time}**. {toggle_notice}", + "location_name_empty": "Не вдалося знайти жодного населеного пункту з такою назвою. Спробуйте перефразувати назву або переконайтеся, що Ви використовуєте ту саму мову та назву, що й місцева влада у графіку вивезення сміття.\n\n{cancel_notice}", + "cancelled": "Операцію скасовано.", + "checkout": "Це фактично всі дані, які має бот. Будь ласка, використовуйте ці кнопки, щоб вибрати, чи хочете Ви видалити свої дані з бота.", + "commands_removed": "✅ Всі зареєстровані на цю мить команди було видалено. Команди будуть зареєстровані знову при запуску бота.", + "import_finished": "Ви успішно вставили {count} записів.", + "import_invalid_date": "Записи містять невірні формати дат. Використовуйте формат дати **ISO 8601**.", + "import_invalid_filetype": "Неправильний ввід. Будь ласка, надішліть мені JSON-файл із записами. {cancel_notice}", + "import_invalid": "Це недійсний JSON даних збору сміття.", + "location_name_invalid": "Будь ласка, надішліть назву місця у вигляді тексту. {cancel_notice}", + "location_select": "Виберіть місце за допомогою показаної клавіатури.", + "reminder": "**Вивіз сміття**\n\nТип: {type}\nДата: {date}\n\nНе забудьте підготувати свій контейнер до збору!", + "search_nearby_empty": "Не вдалося знайти жодної локації поблизу. Спробуємо скористатися пошуком за назвою.", + "selection_invalid": "Будь ласка, виберіть правильний варіант за допомогою клавіатури. {cancel_notice}", + "set_offset_invalid": "Будь ласка, вкажіть дійсну цілу кількість днів у діапазоні від 0 до 7 (включно). {cancel_notice}", + "set_offset": "Гаразд. Будь ласка, напишіть, за скільки днів до збору ви хочете отримати сповіщення про збір.", + "import": "Гаразд. Надішліть правильний JSON.", + "set_time_invalid": "Будь ласка, вкажіть дійсний час у форматі ГГ:ХХ. {cancel_notice}", + "setup_retry": "ℹ️ Якщо Ви захочете вибрати місце розташування, скористайтеся командою /setup.", + "start_code": "ℹ️ Ви запустили бота за посиланням, що містить локацію **{name}**.\n\nБудь ласка, підтвердіть, чи хочете Ви використовувати її як свою локацію для сповіщень.", + "start_configure": "📍 Налаштуймо Вашу локацію. Натисніть кнопку на показаній клавіатурі, щоб почати процес.", + "toggle_disabled": "🔕 Сповіщення було вимкнено.", + "toggle_enabled_location": "🔔 Сповіщення увімкнено за {offset} д. до вивезення сміття о {time} для локації **{name}**.", + "toggle_enabled": "🔔 Сповіщення було увімкнено за {offset} д. до вивезення сміття о {time}. Оберіть своє розташування за допомогою /setup.", + "toggle": "Використовуйте /toggle, щоб увімкнути сповіщення.", + "upcoming_empty_location": "У Вас не встановлено локацію вивозу. Оберіть свою локацію за допомогою /setup.", + "upcoming": "Найближчі вивози сміття:\n\n{entries}" }, "buttons": { - "configure": "Давайте налаштуємо бота" + "configure": "Давайте налаштуємо бота", + "search_nearby": "📍 Пошук найближчих локацій", + "delete_confirm": "Я погоджуюсь і хочу продовжити", + "delete_no": "❌ Ні, я не хочу видаляти їх", + "delete_yes": "✅ Так, я хочу видалити їх", + "search_name": "🔎 Пошук за назвою місця", + "start_code_no": "❌ Ні, я не хочу її використовувати", + "start_code_yes": "✅ Так, я хочу її використовувати", + "start_configure": "⚙️ Налаштуймо бота" }, "callbacks": { "locale_set": "Встановлено мову: {locale}" }, - "force_replies": {} -} \ No newline at end of file + "force_replies": { + "import": "JSON із записами вивозу сміття", + "location_name": "Назва локації", + "set_offset": "Кількість днів", + "set_time": "Час у вигляді ГГ:ХХ" + } +} From 05f4765d99b2acd550eebe5d1ad536860c49d341 Mon Sep 17 00:00:00 2001 From: profitroll Date: Wed, 30 Aug 2023 12:13:40 +0200 Subject: [PATCH 05/36] Fixed wrong key used in /setup --- plugins/commands/setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/commands/setup.py b/plugins/commands/setup.py index 3b1d2ec..a82acf8 100644 --- a/plugins/commands/setup.py +++ b/plugins/commands/setup.py @@ -15,7 +15,7 @@ logger = logging.getLogger(__name__) @PyroClient.on_message( - ~filters.scheduled & filters.private & filters.command(["setup"] + i18n.sync.in_all_locales("configure", "buttons"), prefixes=["/", ""]) # type: ignore + ~filters.scheduled & filters.private & filters.command(["setup"] + i18n.sync.in_all_locales("start_configure", "buttons"), prefixes=["/", ""]) # type: ignore ) async def command_setup(app: PyroClient, message: Message): user = await app.find_user(message.from_user) From 974d59595bc409f36fe46dc4eb18c2d80082942d Mon Sep 17 00:00:00 2001 From: Weblate Date: Wed, 30 Aug 2023 13:36:01 +0300 Subject: [PATCH 06/36] Translated using Weblate (Ukrainian) Currently translated at 100.0% (78 of 78 strings) Co-authored-by: Profitroll Translate-URL: https://weblate.end-play.xyz/projects/garbagereminder/telegrambot/uk/ Translation: GarbageReminder/TelegramBot --- locale/uk.json | 74 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 63 insertions(+), 11 deletions(-) diff --git a/locale/uk.json b/locale/uk.json index 718bed6..596a92e 100644 --- a/locale/uk.json +++ b/locale/uk.json @@ -21,27 +21,79 @@ }, "commands": { "help": "Показати меню допомоги", - "setup": "Обрати місто та район", - "toggle": "Вимкнути/вимкнути повідомлення", - "set_time": "Встановити час повідомлень", - "set_offset": "Встановити дні випередження", - "upcoming": "Дати збирання на наступні 30 днів", + "setup": "Обрати місце розташування", + "toggle": "Увімкнути/вимкнути сповіщення", + "set_time": "Встановити час сповіщень", + "set_offset": "Встановити зміщення сповіщень", + "upcoming": "Дати збору на наступні 30 днів", "language": "Змінити мову повідомлень бота", - "checkout": "Експортувати чи видалити дані", + "checkout": "Експортувати або видалити дані", "import": "Завантажити з JSON до бази даних", "shutdown": "Вимкнути бота", "remove_commands": "Видалити всі команди" }, "messages": { - "help": "Привіт! Я твій бот!", + "help": "🔔 Цей бот надсилає сповіщення про вивезення сміття згідно з Вашим місцевим графіком.\n\n**Доступні команди**\n/help - Показати це повідомлення\n/setup - Вибрати місце розташування\n/toggle - Увімкнути/вимкнути нагадування\n/set_time - Встановити час нагадувань\n/set_offset - Встановити зсув між нагадуваннями та вивозом\n/upcoming - Показати майбутні вивезення\n/language - Обрати мову бота\n/checkout - Експортувати або видалити дані\n\nВи також можете запропонувати додати своє місто/район до бота, зв'язавшись з адміністраторами за [цим посиланням]({url_contact}) та вказавши свій розклад.\n\n⚙️ Бажаєте розмістити цього бота самостійно або внести деякі зміни? Бот має відкритий вихідний код, тож Ви можете форкнути його. Ознайомтесь із [репозиторієм бота]({url_repo}) щоб дізнатись деталі.\n\nПриємного користування! 🤗", "start": "👋 Вітання!\n\nЦей невеличкий бот з відкритим вихідним кодом створений для того, щоб трохи спростити Вам життя, надсилаючи сповіщення про вивіз сміття у вашому регіоні.\n\nКористуючись цим ботом, Ви приймаєте [Політику конфіденційності]({privacy_policy}), в іншому випадку, будь ласка, заблокуйте та видаліть цього бота перед подальшою взаємодією.\n\nТепер офіційна частина закінчена, тож Ви можете зануритися в бота.", - "locale_choice": "Гаразд. Будь ласка, оберіть мову за допомогою клавіатури нижче." + "locale_choice": "Гаразд. Будь ласка, оберіть мову за допомогою клавіатури нижче.", + "set_time": "Гаразд. Будь ласка, надішліть бажаний час у форматі ГГ:ХХ.", + "setup_finished": "✅ Готово! Ваше місцезнаходження тепер **{ім'я}**. Ви будете отримувати сповіщення про вивезення сміття за {offset} д. заздалегідь о {time}.", + "import_finished": "Ви успішно вставили {count} записів.", + "setup": "⚙️ Почнемо налаштування з пошуку Вашого місцезнаходження.\n\nБудь ласка, виберіть, чи хочете Ви шукати серед найближчих до Вас локацій, чи одразу перейти до пошуку за назвою.\n\nЗверніть увагу, що надіслане Вами місцезнаходження **НЕ** зберігається ніде і використовується лише для пошуку місць поряд в базі даних.", + "set_offset_finished": "🔔 Час сповіщень було оновлено! Тепер Ви будете отримувати сповіщення про вивіз сміття за **{offset} д.** до вивозу о {time}. {toggle_notice}", + "set_offset_invalid": "Будь ласка, вкажіть дійсну цілу кількість днів у діапазоні від 0 до 7 (включно). {cancel_notice}", + "set_offset": "Гаразд. Будь ласка, напишіть, за скільки днів до збору Ви хочете отримати сповіщення про збір.", + "cancelled": "Операцію скасовано.", + "checkout_deletion": "Гаразд. Будь ласка, підтвердіть, що Ви хочете видалити свої дані з бота.\n\nНаступні дані будуть видалені:\n• Вибране місце\n• Бажана мова всіх повідомлень\n• Час сповіщень\n• Зсув сповіщень\n\nВикористовуйте клавіатуру, щоб підтвердити й продовжити або /cancel, щоб перервати цю операцію.", + "checkout": "Це фактично всі дані, які має бот. Будь ласка, використовуйте ці кнопки, щоб вибрати, чи хочете Ви видалити свої дані з бота.", + "import_invalid_date": "Записи містять невірні формати дат. Використовуйте формат дати **ISO 8601**.", + "import_invalid_filetype": "Неправильний ввід. Будь ласка, надішліть мені JSON-файл із записами. {cancel_notice}", + "import_invalid": "Це недійсний JSON даних збору сміття.", + "import": "Гаразд. Надішліть правильний JSON.", + "location_name_empty": "Не вдалося знайти жодного населеного пункту з такою назвою. Спробуйте перефразувати назву або переконайтеся, що Ви використовуєте ту саму мову та назву, що й місцева влада у графіку вивезення сміття.\n\n{cancel_notice}", + "location_name_invalid": "Будь ласка, надішліть назву місця у вигляді тексту. {cancel_notice}", + "location_name": "Будь ласка, надішліть мені назву населеного пункту. Це має бути назва, яка використовується у графіку вивезення сміття Вашою місцевою владою. Зазвичай це назва району або міста.", + "location_select": "Виберіть місце за допомогою показаної клавіатури.", + "reminder": "**Вивіз сміття**\n\nТип: {type}\nДата: {date}\n\nНе забудьте підготувати свій контейнер до збору!", + "search_nearby_empty": "Не вдалося знайти жодної локації поблизу. Спробуємо скористатися пошуком за назвою.", + "selection_invalid": "Будь ласка, виберіть правильний варіант за допомогою клавіатури. {cancel_notice}", + "set_time_finished": "🔔 Час сповіщень було оновлено! Тепер Ви будете отримувати сповіщення про вивіз сміття за {offset} д. до вивозу о **{time}**. {toggle_notice}", + "set_time_invalid": "Будь ласка, вкажіть дійсний час у форматі ГГ:ХХ. {cancel_notice}", + "setup_retry": "ℹ️ Якщо Ви захочете вибрати місце розташування, скористайтеся командою /setup.", + "start_code_invalid": "🚫 Ви запустили бота за посиланням, що містить локацію, але, схоже, вона не є дійсною. Будь ласка, скористайтеся командою /setup, щоб налаштувати локацію вручну.", + "start_code": "ℹ️ Ви запустили бота за посиланням, що містить локацію **{name}**.\n\nБудь ласка, підтвердіть, чи хочете Ви використовувати її як свою локацію для сповіщень.", + "start_configure": "📍 Налаштуймо Вашу локацію. Натисніть кнопку на показаній клавіатурі, щоб почати процес.", + "start_selection_no": "Гаразд, тепер Ви самі по собі. Будь ласка, скористайтеся командою /setup, щоб налаштувати своє місцезнаходження і почати отримувати нагадування.", + "start_selection_yes": "✅ Готово! Ваша локація тепер **{name}**. Ви будете отримувати нагадування про вивезення сміття за {offset} д. заздалегідь о {time}.\n\nБудь ласка, скористайтесь /help, якщо Ви хочете дізнатися, як змінити час сповіщень або вимкнути їх.", + "toggle_disabled": "🔕 Сповіщення було вимкнено.", + "toggle_enabled_location": "🔔 Сповіщення увімкнено за {offset} д. до вивезення сміття о {time} для локації **{name}**.", + "toggle_enabled": "🔔 Сповіщення було увімкнено за {offset} д. до вивезення сміття о {time}. Оберіть своє розташування за допомогою /setup.", + "toggle": "Використовуйте /toggle, щоб увімкнути сповіщення.", + "upcoming_empty_location": "У Вас не встановлено локацію вивозу. Оберіть свою локацію за допомогою /setup.", + "upcoming": "Найближчі вивози сміття:\n\n{entries}", + "upcoming_empty": "Не знайдено записів про вивезення сміття на найближчі 30 днів для **{name}**", + "cancel": "Якщо Ви хочете скасувати цю операцію, використовуйте /cancel.", + "checkout_deleted": "🗑️ Ваші дані було видалено. Якщо Ви хочете знову почати користуватися цим ботом, скористайтеся командою /setup. В іншому випадку видаліть/заблокуйте бота і більше не взаємодійте з ним.", + "commands_removed": "✅ Всі зареєстровані на цю мить команди було видалено. Команди будуть зареєстровані знову при запуску бота." }, "buttons": { - "configure": "Давайте налаштуємо бота" + "configure": "Давайте налаштуємо бота", + "delete_confirm": "Я погоджуюсь і хочу продовжити", + "delete_no": "❌ Ні, я не хочу видаляти їх", + "delete_yes": "✅ Так, я хочу видалити їх", + "search_name": "🔎 Пошук за назвою місця", + "start_code_no": "❌ Ні, я не хочу її використовувати", + "start_code_yes": "✅ Так, я хочу її використовувати", + "start_configure": "⚙️ Налаштуймо бота", + "search_nearby": "📍 Пошук найближчих локацій" }, "callbacks": { "locale_set": "Встановлено мову: {locale}" }, - "force_replies": {} -} \ No newline at end of file + "force_replies": { + "import": "JSON із записами вивозу сміття", + "set_offset": "Кількість днів", + "set_time": "Час у вигляді ГГ:ХХ", + "location_name": "Назва локації" + } +} From 2c7916b8866646bb94e5f6418659a1c13096636e Mon Sep 17 00:00:00 2001 From: profitroll Date: Wed, 30 Aug 2023 12:37:09 +0200 Subject: [PATCH 07/36] Fixed locale strings --- locale/de.json | 1 - modules/search_name.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 locale/de.json diff --git a/locale/de.json b/locale/de.json deleted file mode 100644 index 0967ef4..0000000 --- a/locale/de.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/modules/search_name.py b/modules/search_name.py index 8d497be..1f3caf7 100644 --- a/modules/search_name.py +++ b/modules/search_name.py @@ -15,7 +15,7 @@ async def search_name(app: PyroClient, message: Message) -> Union[Location, None location: Union[Location, None] = None await message.reply_text( - app._("location_request_name", "messages", locale=user.locale), + app._("location_name", "messages", locale=user.locale), reply_markup=ForceReply( placeholder=app._("location_name", "force_replies", locale=user.locale) ), From 17297786fcd28c481f574390d4763d7b5e5460a9 Mon Sep 17 00:00:00 2001 From: Weblate Date: Wed, 30 Aug 2023 13:40:29 +0300 Subject: [PATCH 08/36] Translated using Weblate (Ukrainian) Currently translated at 100.0% (78 of 78 strings) Co-authored-by: Profitroll Translate-URL: https://weblate.end-play.xyz/projects/garbagereminder/telegrambot/uk/ Translation: GarbageReminder/TelegramBot --- locale/uk.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locale/uk.json b/locale/uk.json index 596a92e..ea168b9 100644 --- a/locale/uk.json +++ b/locale/uk.json @@ -37,7 +37,7 @@ "start": "👋 Вітання!\n\nЦей невеличкий бот з відкритим вихідним кодом створений для того, щоб трохи спростити Вам життя, надсилаючи сповіщення про вивіз сміття у вашому регіоні.\n\nКористуючись цим ботом, Ви приймаєте [Політику конфіденційності]({privacy_policy}), в іншому випадку, будь ласка, заблокуйте та видаліть цього бота перед подальшою взаємодією.\n\nТепер офіційна частина закінчена, тож Ви можете зануритися в бота.", "locale_choice": "Гаразд. Будь ласка, оберіть мову за допомогою клавіатури нижче.", "set_time": "Гаразд. Будь ласка, надішліть бажаний час у форматі ГГ:ХХ.", - "setup_finished": "✅ Готово! Ваше місцезнаходження тепер **{ім'я}**. Ви будете отримувати сповіщення про вивезення сміття за {offset} д. заздалегідь о {time}.", + "setup_finished": "✅ Готово! Ваше місцезнаходження тепер **{name}**. Ви будете отримувати сповіщення про вивезення сміття за {offset} д. заздалегідь о {time}.", "import_finished": "Ви успішно вставили {count} записів.", "setup": "⚙️ Почнемо налаштування з пошуку Вашого місцезнаходження.\n\nБудь ласка, виберіть, чи хочете Ви шукати серед найближчих до Вас локацій, чи одразу перейти до пошуку за назвою.\n\nЗверніть увагу, що надіслане Вами місцезнаходження **НЕ** зберігається ніде і використовується лише для пошуку місць поряд в базі даних.", "set_offset_finished": "🔔 Час сповіщень було оновлено! Тепер Ви будете отримувати сповіщення про вивіз сміття за **{offset} д.** до вивозу о {time}. {toggle_notice}", From c67f29a5cd0d8a49fb96f274c861f5c63c9d80e8 Mon Sep 17 00:00:00 2001 From: profitroll Date: Wed, 30 Aug 2023 12:46:53 +0200 Subject: [PATCH 09/36] Sort the lines --- locale/uk.json | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/locale/uk.json b/locale/uk.json index 264c386..263a3c0 100644 --- a/locale/uk.json +++ b/locale/uk.json @@ -33,24 +33,19 @@ "remove_commands": "Видалити всі команди" }, "messages": { - "help": "🔔 Цей бот надсилає сповіщення про вивезення сміття згідно з Вашим місцевим графіком.\n\n**Доступні команди**\n/help - Показати це повідомлення\n/setup - Вибрати місце розташування\n/toggle - Увімкнути/вимкнути нагадування\n/set_time - Встановити час нагадувань\n/set_offset - Встановити зсув між нагадуваннями та вивозом\n/upcoming - Показати майбутні вивезення\n/language - Обрати мову бота\n/checkout - Експортувати або видалити дані\n\nВи також можете запропонувати додати своє місто/район до бота, зв'язавшись з адміністраторами за [цим посиланням]({url_contact}) та вказавши свій розклад.\n\n⚙️ Бажаєте розмістити цього бота самостійно або внести деякі зміни? Бот має відкритий вихідний код, тож ви можете форкнути його. Ознайомтесь із [репозиторієм бота]({url_repo}) щоб дізнатись деталі.\n\nПриємного користування! 🤗", - "help": "🔔 Цей бот надсилає сповіщення про вивезення сміття згідно з Вашим місцевим графіком.\n\n**Доступні команди**\n/help - Показати це повідомлення\n/setup - Вибрати місце розташування\n/toggle - Увімкнути/вимкнути нагадування\n/set_time - Встановити час нагадувань\n/set_offset - Встановити зсув між нагадуваннями та вивозом\n/upcoming - Показати майбутні вивезення\n/language - Обрати мову бота\n/checkout - Експортувати або видалити дані\n\nВи також можете запропонувати додати своє місто/район до бота, зв'язавшись з адміністраторами за [цим посиланням]({url_contact}) та вказавши свій розклад.\n\n⚙️ Бажаєте розмістити цього бота самостійно або внести деякі зміни? Бот має відкритий вихідний код, тож Ви можете форкнути його. Ознайомтесь із [репозиторієм бота]({url_repo}) щоб дізнатись деталі.\n\nПриємного користування! 🤗", - "start": "👋 Вітання!\n\nЦей невеличкий бот з відкритим вихідним кодом створений для того, щоб трохи спростити Вам життя, надсилаючи сповіщення про вивіз сміття у вашому регіоні.\n\nКористуючись цим ботом, Ви приймаєте [Політику конфіденційності]({privacy_policy}), в іншому випадку, будь ласка, заблокуйте та видаліть цього бота перед подальшою взаємодією.\n\nТепер офіційна частина закінчена, тож Ви можете зануритися в бота.", - "locale_choice": "Гаразд. Будь ласка, оберіть мову за допомогою клавіатури нижче.", - "set_time": "Гаразд. Будь ласка, надішліть бажаний час у форматі ГГ:ХХ.", - "setup_finished": "✅ Готово! Ваше місцезнаходження тепер **{name}**. Ви будете отримувати сповіщення про вивезення сміття за {offset} д. заздалегідь о {time}.", - "import_finished": "Ви успішно вставили {count} записів.", - "setup": "⚙️ Почнемо налаштування з пошуку Вашого місцезнаходження.\n\nБудь ласка, виберіть, чи хочете Ви шукати серед найближчих до Вас локацій, чи одразу перейти до пошуку за назвою.\n\nЗверніть увагу, що надіслане Вами місцезнаходження **НЕ** зберігається ніде і використовується лише для пошуку місць поряд в базі даних.", - "set_offset_finished": "🔔 Час сповіщень було оновлено! Тепер Ви будете отримувати сповіщення про вивіз сміття за **{offset} д.** до вивозу о {time}. {toggle_notice}", - "set_offset_invalid": "Будь ласка, вкажіть дійсну цілу кількість днів у діапазоні від 0 до 7 (включно). {cancel_notice}", - "set_offset": "Гаразд. Будь ласка, напишіть, за скільки днів до збору Ви хочете отримати сповіщення про збір.", + "cancel": "Якщо Ви хочете скасувати цю операцію, використовуйте /cancel.", "cancelled": "Операцію скасовано.", + "checkout_deleted": "🗑️ Ваші дані було видалено. Якщо Ви хочете знову почати користуватися цим ботом, скористайтеся командою /setup. В іншому випадку видаліть/заблокуйте бота і більше не взаємодійте з ним.", "checkout_deletion": "Гаразд. Будь ласка, підтвердіть, що Ви хочете видалити свої дані з бота.\n\nНаступні дані будуть видалені:\n• Вибране місце\n• Бажана мова всіх повідомлень\n• Час сповіщень\n• Зсув сповіщень\n\nВикористовуйте клавіатуру, щоб підтвердити й продовжити або /cancel, щоб перервати цю операцію.", "checkout": "Це фактично всі дані, які має бот. Будь ласка, використовуйте ці кнопки, щоб вибрати, чи хочете Ви видалити свої дані з бота.", + "commands_removed": "✅ Всі зареєстровані на цю мить команди було видалено. Команди будуть зареєстровані знову при запуску бота.", + "help": "🔔 Цей бот надсилає сповіщення про вивезення сміття згідно з Вашим місцевим графіком.\n\n**Доступні команди**\n/help - Показати це повідомлення\n/setup - Вибрати місце розташування\n/toggle - Увімкнути/вимкнути нагадування\n/set_time - Встановити час нагадувань\n/set_offset - Встановити зсув між нагадуваннями та вивозом\n/upcoming - Показати майбутні вивезення\n/language - Обрати мову бота\n/checkout - Експортувати або видалити дані\n\nВи також можете запропонувати додати своє місто/район до бота, зв'язавшись з адміністраторами за [цим посиланням]({url_contact}) та вказавши свій розклад.\n\n⚙️ Бажаєте розмістити цього бота самостійно або внести деякі зміни? Бот має відкритий вихідний код, тож ви можете форкнути його. Ознайомтесь із [репозиторієм бота]({url_repo}) щоб дізнатись деталі.\n\nПриємного користування! 🤗", + "import_finished": "Ви успішно вставили {count} записів.", "import_invalid_date": "Записи містять невірні формати дат. Використовуйте формат дати **ISO 8601**.", "import_invalid_filetype": "Неправильний ввід. Будь ласка, надішліть мені JSON-файл із записами. {cancel_notice}", "import_invalid": "Це недійсний JSON даних збору сміття.", "import": "Гаразд. Надішліть правильний JSON.", + "locale_choice": "Гаразд. Будь ласка, оберіть мову за допомогою клавіатури нижче.", "location_name_empty": "Не вдалося знайти жодного населеного пункту з такою назвою. Спробуйте перефразувати назву або переконайтеся, що Ви використовуєте ту саму мову та назву, що й місцева влада у графіку вивезення сміття.\n\n{cancel_notice}", "location_name_invalid": "Будь ласка, надішліть назву місця у вигляді тексту. {cancel_notice}", "location_name": "Будь ласка, надішліть мені назву населеного пункту. Це має бути назва, яка використовується у графіку вивезення сміття Вашою місцевою владою. Зазвичай це назва району або міста.", @@ -58,24 +53,28 @@ "reminder": "**Вивіз сміття**\n\nТип: {type}\nДата: {date}\n\nНе забудьте підготувати свій контейнер до збору!", "search_nearby_empty": "Не вдалося знайти жодної локації поблизу. Спробуємо скористатися пошуком за назвою.", "selection_invalid": "Будь ласка, виберіть правильний варіант за допомогою клавіатури. {cancel_notice}", + "set_offset_finished": "🔔 Час сповіщень було оновлено! Тепер Ви будете отримувати сповіщення про вивіз сміття за **{offset} д.** до вивозу о {time}. {toggle_notice}", + "set_offset_invalid": "Будь ласка, вкажіть дійсну цілу кількість днів у діапазоні від 0 до 7 (включно). {cancel_notice}", + "set_offset": "Гаразд. Будь ласка, напишіть, за скільки днів до збору Ви хочете отримати сповіщення про збір.", "set_time_finished": "🔔 Час сповіщень було оновлено! Тепер Ви будете отримувати сповіщення про вивіз сміття за {offset} д. до вивозу о **{time}**. {toggle_notice}", "set_time_invalid": "Будь ласка, вкажіть дійсний час у форматі ГГ:ХХ. {cancel_notice}", + "set_time": "Гаразд. Будь ласка, надішліть бажаний час у форматі ГГ:ХХ.", + "setup_finished": "✅ Готово! Ваше місцезнаходження тепер **{name}**. Ви будете отримувати сповіщення про вивезення сміття за {offset} д. заздалегідь о {time}.", "setup_retry": "ℹ️ Якщо Ви захочете вибрати місце розташування, скористайтеся командою /setup.", + "setup": "⚙️ Почнемо налаштування з пошуку Вашого місцезнаходження.\n\nБудь ласка, виберіть, чи хочете Ви шукати серед найближчих до Вас локацій, чи одразу перейти до пошуку за назвою.\n\nЗверніть увагу, що надіслане Вами місцезнаходження **НЕ** зберігається ніде і використовується лише для пошуку місць поряд в базі даних.", "start_code_invalid": "🚫 Ви запустили бота за посиланням, що містить локацію, але, схоже, вона не є дійсною. Будь ласка, скористайтеся командою /setup, щоб налаштувати локацію вручну.", "start_code": "ℹ️ Ви запустили бота за посиланням, що містить локацію **{name}**.\n\nБудь ласка, підтвердіть, чи хочете Ви використовувати її як свою локацію для сповіщень.", "start_configure": "📍 Налаштуймо Вашу локацію. Натисніть кнопку на показаній клавіатурі, щоб почати процес.", "start_selection_no": "Гаразд, тепер Ви самі по собі. Будь ласка, скористайтеся командою /setup, щоб налаштувати своє місцезнаходження і почати отримувати нагадування.", "start_selection_yes": "✅ Готово! Ваша локація тепер **{name}**. Ви будете отримувати нагадування про вивезення сміття за {offset} д. заздалегідь о {time}.\n\nБудь ласка, скористайтесь /help, якщо Ви хочете дізнатися, як змінити час сповіщень або вимкнути їх.", + "start": "👋 Вітання!\n\nЦей невеличкий бот з відкритим вихідним кодом створений для того, щоб трохи спростити Вам життя, надсилаючи сповіщення про вивіз сміття у вашому регіоні.\n\nКористуючись цим ботом, Ви приймаєте [Політику конфіденційності]({privacy_policy}), в іншому випадку, будь ласка, заблокуйте та видаліть цього бота перед подальшою взаємодією.\n\nТепер офіційна частина закінчена, тож Ви можете зануритися в бота.", "toggle_disabled": "🔕 Сповіщення було вимкнено.", "toggle_enabled_location": "🔔 Сповіщення увімкнено за {offset} д. до вивезення сміття о {time} для локації **{name}**.", "toggle_enabled": "🔔 Сповіщення було увімкнено за {offset} д. до вивезення сміття о {time}. Оберіть своє розташування за допомогою /setup.", "toggle": "Використовуйте /toggle, щоб увімкнути сповіщення.", "upcoming_empty_location": "У Вас не встановлено локацію вивозу. Оберіть свою локацію за допомогою /setup.", - "upcoming": "Найближчі вивози сміття:\n\n{entries}", "upcoming_empty": "Не знайдено записів про вивезення сміття на найближчі 30 днів для **{name}**", - "cancel": "Якщо Ви хочете скасувати цю операцію, використовуйте /cancel.", - "checkout_deleted": "🗑️ Ваші дані було видалено. Якщо Ви хочете знову почати користуватися цим ботом, скористайтеся командою /setup. В іншому випадку видаліть/заблокуйте бота і більше не взаємодійте з ним.", - "commands_removed": "✅ Всі зареєстровані на цю мить команди було видалено. Команди будуть зареєстровані знову при запуску бота." + "upcoming": "Найближчі вивози сміття:\n\n{entries}" }, "buttons": { "configure": "Давайте налаштуємо бота", @@ -83,18 +82,18 @@ "delete_no": "❌ Ні, я не хочу видаляти їх", "delete_yes": "✅ Так, я хочу видалити їх", "search_name": "🔎 Пошук за назвою місця", + "search_nearby": "📍 Пошук найближчих локацій", "start_code_no": "❌ Ні, я не хочу її використовувати", "start_code_yes": "✅ Так, я хочу її використовувати", - "start_configure": "⚙️ Налаштуймо бота", - "search_nearby": "📍 Пошук найближчих локацій" + "start_configure": "⚙️ Налаштуймо бота" }, "callbacks": { "locale_set": "Встановлено мову: {locale}" }, "force_replies": { "import": "JSON із записами вивозу сміття", + "location_name": "Назва локації", "set_offset": "Кількість днів", - "set_time": "Час у вигляді ГГ:ХХ", - "location_name": "Назва локації" + "set_time": "Час у вигляді ГГ:ХХ" } } \ No newline at end of file From 0b720ef770b5c9091039ebbf3cefa9b3aab4f36f Mon Sep 17 00:00:00 2001 From: profitroll Date: Wed, 30 Aug 2023 12:52:31 +0200 Subject: [PATCH 10/36] OperationFailure handling added --- plugins/commands/upcoming.py | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/plugins/commands/upcoming.py b/plugins/commands/upcoming.py index 262e53b..b96665f 100644 --- a/plugins/commands/upcoming.py +++ b/plugins/commands/upcoming.py @@ -1,5 +1,6 @@ from datetime import datetime, timedelta, timezone +from pymongo.errors import OperationFailure from pyrogram import filters from pyrogram.types import Message from pytz import timezone as pytz_timezone @@ -33,15 +34,23 @@ async def command_upcoming(app: PyroClient, message: Message): + timedelta(days=30) ).replace(tzinfo=timezone.utc) - entries = [ - await GarbageEntry.from_record(entry) - async for entry in col_entries.find( - { - "location": {"$in": user.location.id}, - "date": {"$gte": date_min, "$lte": date_max}, - } + try: + entries = [ + await GarbageEntry.from_record(entry) + async for entry in col_entries.find( + { + "location": {"$in": user.location.id}, + "date": {"$gte": date_min, "$lte": date_max}, + } + ) + ] + except OperationFailure: + await message.reply_text( + app._("upcoming_empty", "messages", locale=user.locale).format( + name=user.location.name + ) ) - ] + return entries_text = "\n\n".join( [ From c303f620050b9685eb40520230db695de085fc92 Mon Sep 17 00:00:00 2001 From: profitroll Date: Wed, 30 Aug 2023 12:56:20 +0200 Subject: [PATCH 11/36] Fixed "location" instead of "locations" --- modules/reminder.py | 2 +- plugins/commands/upcoming.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/reminder.py b/modules/reminder.py index b845303..134b0e2 100644 --- a/modules/reminder.py +++ b/modules/reminder.py @@ -39,7 +39,7 @@ async def remind(app: PyroClient) -> None: entries = await col_entries.find( { - "location": {"$in": location.id}, + "locations": {"$in": location.id}, "date": user_date.replace(hour=0, minute=0), } ).to_list() diff --git a/plugins/commands/upcoming.py b/plugins/commands/upcoming.py index b96665f..c5866c5 100644 --- a/plugins/commands/upcoming.py +++ b/plugins/commands/upcoming.py @@ -39,7 +39,7 @@ async def command_upcoming(app: PyroClient, message: Message): await GarbageEntry.from_record(entry) async for entry in col_entries.find( { - "location": {"$in": user.location.id}, + "locations": {"$in": user.location.id}, "date": {"$gte": date_min, "$lte": date_max}, } ) From 42ca71aa3e1d6e16a68b8f3c4a99b5a349cfd94d Mon Sep 17 00:00:00 2001 From: profitroll Date: Wed, 30 Aug 2023 13:00:21 +0200 Subject: [PATCH 12/36] Fixed wrong $in usage --- modules/reminder.py | 2 +- plugins/commands/upcoming.py | 24 ++++++++---------------- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/modules/reminder.py b/modules/reminder.py index 134b0e2..cf2b939 100644 --- a/modules/reminder.py +++ b/modules/reminder.py @@ -39,7 +39,7 @@ async def remind(app: PyroClient) -> None: entries = await col_entries.find( { - "locations": {"$in": location.id}, + "locations": location.id, "date": user_date.replace(hour=0, minute=0), } ).to_list() diff --git a/plugins/commands/upcoming.py b/plugins/commands/upcoming.py index c5866c5..ac29e63 100644 --- a/plugins/commands/upcoming.py +++ b/plugins/commands/upcoming.py @@ -34,23 +34,15 @@ async def command_upcoming(app: PyroClient, message: Message): + timedelta(days=30) ).replace(tzinfo=timezone.utc) - try: - entries = [ - await GarbageEntry.from_record(entry) - async for entry in col_entries.find( - { - "locations": {"$in": user.location.id}, - "date": {"$gte": date_min, "$lte": date_max}, - } - ) - ] - except OperationFailure: - await message.reply_text( - app._("upcoming_empty", "messages", locale=user.locale).format( - name=user.location.name - ) + entries = [ + await GarbageEntry.from_record(entry) + async for entry in col_entries.find( + { + "locations": user.location.id, + "date": {"$gte": date_min, "$lte": date_max}, + } ) - return + ] entries_text = "\n\n".join( [ From 1248efd20fcf1c4c3e45a133ae8f4191e873d260 Mon Sep 17 00:00:00 2001 From: profitroll Date: Wed, 30 Aug 2023 14:01:30 +0200 Subject: [PATCH 13/36] Fixed results order --- modules/search_name.py | 2 +- modules/search_nearby.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/search_name.py b/modules/search_name.py index 1f3caf7..7c918c4 100644 --- a/modules/search_name.py +++ b/modules/search_name.py @@ -46,7 +46,7 @@ async def search_name(app: PyroClient, message: Message) -> Union[Location, None query = {"$text": {"$search": answer.text}} - locations = await col_locations.find(query).limit(6).to_list() + locations = (await col_locations.find(query).limit(6).to_list()).reverse() if len(locations) == 0: await message.reply_text( diff --git a/modules/search_nearby.py b/modules/search_nearby.py index be94d8e..332a9ea 100644 --- a/modules/search_nearby.py +++ b/modules/search_nearby.py @@ -24,7 +24,7 @@ async def search_nearby(app: PyroClient, message: Message) -> Union[Location, No } } - locations = await col_locations.find(query).limit(6).to_list() + locations = (await col_locations.find(query).limit(6).to_list()).reverse() if len(locations) == 0: await message.reply_text( From 0006703daf114b5eafd71684ed6812a3cee314ba Mon Sep 17 00:00:00 2001 From: profitroll Date: Wed, 30 Aug 2023 14:03:08 +0200 Subject: [PATCH 14/36] Fixed stupid mistake of using an empty list --- modules/search_name.py | 4 +++- modules/search_nearby.py | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/modules/search_name.py b/modules/search_name.py index 7c918c4..3377021 100644 --- a/modules/search_name.py +++ b/modules/search_name.py @@ -46,7 +46,7 @@ async def search_name(app: PyroClient, message: Message) -> Union[Location, None query = {"$text": {"$search": answer.text}} - locations = (await col_locations.find(query).limit(6).to_list()).reverse() + locations = await col_locations.find(query).limit(6).to_list() if len(locations) == 0: await message.reply_text( @@ -61,6 +61,8 @@ async def search_name(app: PyroClient, message: Message) -> Union[Location, None ) continue + locations = locations.reverse() + keyboard = ReplyKeyboard(resize_keyboard=True, row_width=2) keyboard.add(*[ReplyButton(db_record["name"]) for db_record in locations]) diff --git a/modules/search_nearby.py b/modules/search_nearby.py index 332a9ea..30af99b 100644 --- a/modules/search_nearby.py +++ b/modules/search_nearby.py @@ -24,7 +24,7 @@ async def search_nearby(app: PyroClient, message: Message) -> Union[Location, No } } - locations = (await col_locations.find(query).limit(6).to_list()).reverse() + locations = await col_locations.find(query).limit(6).to_list() if len(locations) == 0: await message.reply_text( @@ -32,6 +32,8 @@ async def search_nearby(app: PyroClient, message: Message) -> Union[Location, No ) return await search_name(app, message) + locations = locations.reverse() + keyboard = ReplyKeyboard(resize_keyboard=True, row_width=2) keyboard.add(*[ReplyButton(db_record["name"]) for db_record in locations]) From a9a92257dc8dbaa48726b4fd581875467118f338 Mon Sep 17 00:00:00 2001 From: profitroll Date: Wed, 30 Aug 2023 14:04:36 +0200 Subject: [PATCH 15/36] None check added --- modules/search_name.py | 2 +- modules/search_nearby.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/search_name.py b/modules/search_name.py index 3377021..9f0ad2e 100644 --- a/modules/search_name.py +++ b/modules/search_name.py @@ -48,7 +48,7 @@ async def search_name(app: PyroClient, message: Message) -> Union[Location, None locations = await col_locations.find(query).limit(6).to_list() - if len(locations) == 0: + if len(locations) == 0 or locations is None: await message.reply_text( app._("location_name_empty", "messages", locale=user.locale).format( cancel_notice=app._("cancel", "messages", locale=user.locale) diff --git a/modules/search_nearby.py b/modules/search_nearby.py index 30af99b..7b95d00 100644 --- a/modules/search_nearby.py +++ b/modules/search_nearby.py @@ -26,7 +26,7 @@ async def search_nearby(app: PyroClient, message: Message) -> Union[Location, No locations = await col_locations.find(query).limit(6).to_list() - if len(locations) == 0: + if len(locations) == 0 or locations is None: await message.reply_text( app._("search_nearby_empty", "messages", locale=user.locale) ) From c91d7f3afa19d384ac188f1bb1b8203274d62e53 Mon Sep 17 00:00:00 2001 From: profitroll Date: Wed, 30 Aug 2023 14:06:46 +0200 Subject: [PATCH 16/36] Fixed reverse assignation and stopped commit-tests --- modules/search_name.py | 2 +- modules/search_nearby.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/search_name.py b/modules/search_name.py index 9f0ad2e..f569407 100644 --- a/modules/search_name.py +++ b/modules/search_name.py @@ -61,7 +61,7 @@ async def search_name(app: PyroClient, message: Message) -> Union[Location, None ) continue - locations = locations.reverse() + locations.reverse() keyboard = ReplyKeyboard(resize_keyboard=True, row_width=2) keyboard.add(*[ReplyButton(db_record["name"]) for db_record in locations]) diff --git a/modules/search_nearby.py b/modules/search_nearby.py index 7b95d00..1681995 100644 --- a/modules/search_nearby.py +++ b/modules/search_nearby.py @@ -26,13 +26,13 @@ async def search_nearby(app: PyroClient, message: Message) -> Union[Location, No locations = await col_locations.find(query).limit(6).to_list() - if len(locations) == 0 or locations is None: + if len(locations) == 0: await message.reply_text( app._("search_nearby_empty", "messages", locale=user.locale) ) return await search_name(app, message) - locations = locations.reverse() + locations.reverse() keyboard = ReplyKeyboard(resize_keyboard=True, row_width=2) keyboard.add(*[ReplyButton(db_record["name"]) for db_record in locations]) From 57ff7b67652873137f1d6fcaa6a803f130498437 Mon Sep 17 00:00:00 2001 From: profitroll Date: Wed, 30 Aug 2023 14:41:34 +0200 Subject: [PATCH 17/36] Context handling implemented --- classes/pyroclient.py | 1 + modules/custom_filters.py | 5 +++++ modules/search_name.py | 4 ++++ modules/search_nearby.py | 3 +++ plugins/commands/checkout.py | 7 ++++++- plugins/commands/help.py | 3 ++- plugins/commands/import.py | 8 ++++++-- plugins/commands/remove_commands.py | 3 ++- plugins/commands/set_offset.py | 5 ++++- plugins/commands/set_time.py | 5 ++++- plugins/commands/setup.py | 5 ++++- plugins/commands/shutdown.py | 4 +++- plugins/commands/start.py | 5 ++++- plugins/commands/toggle.py | 3 ++- plugins/commands/upcoming.py | 4 ++-- plugins/language.py | 3 ++- 16 files changed, 54 insertions(+), 14 deletions(-) diff --git a/classes/pyroclient.py b/classes/pyroclient.py index f4b5ebc..db580c8 100644 --- a/classes/pyroclient.py +++ b/classes/pyroclient.py @@ -18,6 +18,7 @@ class PyroClient(LibPyroClient): self.scheduler.add_job( remind, CronTrigger.from_crontab("* * * * *"), args=(self,) ) + self.contexts = [] async def start(self, **kwargs): await col_locations.create_index( diff --git a/modules/custom_filters.py b/modules/custom_filters.py index 37b019e..4b3c709 100644 --- a/modules/custom_filters.py +++ b/modules/custom_filters.py @@ -10,4 +10,9 @@ async def _owner_func(_, __: PyroClient, message: Message): return False if message.from_user is None else __.owner == message.from_user.id +async def _context_func(_, __: PyroClient, message: Message): + return message.from_user.id in __.contexts + + owner = filters.create(_owner_func) +context = filters.create(_context_func) diff --git a/modules/search_name.py b/modules/search_name.py index f569407..5fae746 100644 --- a/modules/search_name.py +++ b/modules/search_name.py @@ -22,7 +22,9 @@ async def search_name(app: PyroClient, message: Message) -> Union[Location, None ) while location is None: + app.contexts.append(message.from_user.id) answer = await listen_message(app, message.chat.id, 300) + app.contexts.remove(message.from_user.id) if answer is None or answer.text == "/cancel": await message.reply_text( @@ -72,7 +74,9 @@ async def search_name(app: PyroClient, message: Message) -> Union[Location, None ) while True: + app.contexts.append(message.from_user.id) answer = await listen_message(app, message.chat.id, 300) + app.contexts.remove(message.from_user.id) if answer is None or answer.text == "/cancel": await message.reply_text( diff --git a/modules/search_nearby.py b/modules/search_nearby.py index 1681995..32def59 100644 --- a/modules/search_nearby.py +++ b/modules/search_nearby.py @@ -43,7 +43,10 @@ async def search_nearby(app: PyroClient, message: Message) -> Union[Location, No ) while True: + app.contexts.append(message.from_user.id) answer = await listen_message(app, message.chat.id, 300) + app.contexts.remove(message.from_user.id) + location: Union[Location, None] = None if answer is None or answer.text == "/cancel": diff --git a/plugins/commands/checkout.py b/plugins/commands/checkout.py index e5d2528..20210c5 100644 --- a/plugins/commands/checkout.py +++ b/plugins/commands/checkout.py @@ -8,12 +8,13 @@ from pyrogram.types import Message, ReplyKeyboardRemove from ujson import dumps from classes.pyroclient import PyroClient +from modules import custom_filters logger = logging.getLogger(__name__) @PyroClient.on_message( - ~filters.scheduled & filters.private & filters.command(["checkout"], prefixes=["/"]) # type: ignore + ~filters.scheduled & filters.private & filters.command(["checkout"], prefixes=["/"]) & ~custom_filters.context # type: ignore ) async def command_checkout(app: PyroClient, message: Message): user = await app.find_user(message.from_user) @@ -42,7 +43,9 @@ async def command_checkout(app: PyroClient, message: Message): ) while True: + app.contexts.append(message.from_user.id) answer_delete = await listen_message(app, message.chat.id, 300) + app.contexts.remove(message.from_user.id) if answer_delete is None or answer_delete.text == "/cancel": await message.reply_text( @@ -84,7 +87,9 @@ async def command_checkout(app: PyroClient, message: Message): ) while True: + app.contexts.append(message.from_user.id) answer_confirm = await listen_message(app, message.chat.id, 300) + app.contexts.remove(message.from_user.id) if answer_confirm is None or answer_confirm.text == "/cancel": await message.reply_text( diff --git a/plugins/commands/help.py b/plugins/commands/help.py index 6e211e9..f96f56d 100644 --- a/plugins/commands/help.py +++ b/plugins/commands/help.py @@ -2,10 +2,11 @@ from pyrogram import filters from pyrogram.types import Message from classes.pyroclient import PyroClient +from modules import custom_filters @PyroClient.on_message( - ~filters.scheduled & filters.private & filters.command(["help"], prefixes=["/"]) # type: ignore + ~filters.scheduled & filters.private & filters.command(["help"], prefixes=["/"]) & ~custom_filters.context # type: ignore ) async def command_help(app: PyroClient, message: Message): user = await app.find_user(message.from_user) diff --git a/plugins/commands/import.py b/plugins/commands/import.py index df32cb2..64c38e1 100644 --- a/plugins/commands/import.py +++ b/plugins/commands/import.py @@ -12,18 +12,22 @@ from modules.database import col_entries @PyroClient.on_message( - ~filters.scheduled & filters.private & custom_filters.owner & filters.command(["import"], prefixes=["/"]) # type: ignore + ~filters.scheduled & filters.private & custom_filters.owner & filters.command(["import"], prefixes=["/"]) & ~custom_filters.context # type: ignore ) async def command_import(app: PyroClient, message: Message): user = await app.find_user(message.from_user) await message.reply_text( app._("import", "messages", locale=user.locale), - reply_markup=ForceReply(placeholder=""), + reply_markup=ForceReply( + placeholder=app._("import", "force_replies", locale=user.locale) + ), ) while True: + app.contexts.append(message.from_user.id) answer = await listen_message(app, message.chat.id, 300) + app.contexts.remove(message.from_user.id) if answer is None or answer.text == "/cancel": await message.reply_text( diff --git a/plugins/commands/remove_commands.py b/plugins/commands/remove_commands.py index e15ebd7..5541d3e 100644 --- a/plugins/commands/remove_commands.py +++ b/plugins/commands/remove_commands.py @@ -2,10 +2,11 @@ from pyrogram import filters from pyrogram.types import Message from classes.pyroclient import PyroClient +from modules import custom_filters @PyroClient.on_message( - ~filters.scheduled & filters.private & filters.command(["remove_commands"], prefixes=["/"]) # type: ignore + ~filters.scheduled & filters.private & filters.command(["remove_commands"], prefixes=["/"]) & ~custom_filters.context # type: ignore ) async def command_remove_commands(app: PyroClient, message: Message): user = await app.find_user(message.from_user) diff --git a/plugins/commands/set_offset.py b/plugins/commands/set_offset.py index 042c32c..c0981de 100644 --- a/plugins/commands/set_offset.py +++ b/plugins/commands/set_offset.py @@ -6,12 +6,13 @@ from pyrogram import filters from pyrogram.types import ForceReply, Message, ReplyKeyboardRemove from classes.pyroclient import PyroClient +from modules import custom_filters logger = logging.getLogger(__name__) @PyroClient.on_message( - ~filters.scheduled & filters.private & filters.command(["set_offset"], prefixes=["/"]) # type: ignore + ~filters.scheduled & filters.private & filters.command(["set_offset"], prefixes=["/"]) & ~custom_filters.context # type: ignore ) async def command_set_offset(app: PyroClient, message: Message): user = await app.find_user(message.from_user) @@ -24,7 +25,9 @@ async def command_set_offset(app: PyroClient, message: Message): ) while True: + app.contexts.append(message.from_user.id) answer = await listen_message(app, message.chat.id, 300) + app.contexts.remove(message.from_user.id) if answer is None or answer.text == "/cancel": await message.reply_text( diff --git a/plugins/commands/set_time.py b/plugins/commands/set_time.py index b4c6e49..fbee4fb 100644 --- a/plugins/commands/set_time.py +++ b/plugins/commands/set_time.py @@ -6,12 +6,13 @@ from pyrogram import filters from pyrogram.types import ForceReply, Message, ReplyKeyboardRemove from classes.pyroclient import PyroClient +from modules import custom_filters logger = logging.getLogger(__name__) @PyroClient.on_message( - ~filters.scheduled & filters.private & filters.command(["set_time"], prefixes=["/"]) # type: ignore + ~filters.scheduled & filters.private & filters.command(["set_time"], prefixes=["/"]) & ~custom_filters.context # type: ignore ) async def command_set_time(app: PyroClient, message: Message): user = await app.find_user(message.from_user) @@ -24,7 +25,9 @@ async def command_set_time(app: PyroClient, message: Message): ) while True: + app.contexts.append(message.from_user.id) answer = await listen_message(app, message.chat.id, 300) + app.contexts.remove(message.from_user.id) if answer is None or answer.text == "/cancel": await message.reply_text( diff --git a/plugins/commands/setup.py b/plugins/commands/setup.py index a82acf8..5bebffe 100644 --- a/plugins/commands/setup.py +++ b/plugins/commands/setup.py @@ -8,6 +8,7 @@ from pyrogram import filters from pyrogram.types import Message, ReplyKeyboardRemove from classes.pyroclient import PyroClient +from modules import custom_filters from modules.search_name import search_name from modules.search_nearby import search_nearby @@ -15,7 +16,7 @@ logger = logging.getLogger(__name__) @PyroClient.on_message( - ~filters.scheduled & filters.private & filters.command(["setup"] + i18n.sync.in_all_locales("start_configure", "buttons"), prefixes=["/", ""]) # type: ignore + ~filters.scheduled & filters.private & filters.command(["setup"] + i18n.sync.in_all_locales("start_configure", "buttons"), prefixes=["/", ""]) & ~custom_filters.context # type: ignore ) async def command_setup(app: PyroClient, message: Message): user = await app.find_user(message.from_user) @@ -34,7 +35,9 @@ async def command_setup(app: PyroClient, message: Message): ) while True: + app.contexts.append(message.from_user.id) answer_type = await listen_message(app, message.chat.id, 300) + app.contexts.remove(message.from_user.id) if answer_type is None or answer_type.text == "/cancel": await message.reply_text( diff --git a/plugins/commands/shutdown.py b/plugins/commands/shutdown.py index aa971c5..470241c 100644 --- a/plugins/commands/shutdown.py +++ b/plugins/commands/shutdown.py @@ -4,12 +4,14 @@ from pyrogram import filters from pyrogram.types import Message from classes.pyroclient import PyroClient +from modules import custom_filters @PyroClient.on_message( ~filters.scheduled & filters.private - & filters.command(["shutdown", "reboot", "restart"], prefixes=["/"]) # type: ignore + & filters.command(["shutdown", "reboot", "restart"], prefixes=["/"]) + & ~custom_filters.context # type: ignore ) async def command_shutdown(app: PyroClient, msg: Message): if msg.from_user.id == app.owner: diff --git a/plugins/commands/start.py b/plugins/commands/start.py index cb5ee98..3df7e34 100644 --- a/plugins/commands/start.py +++ b/plugins/commands/start.py @@ -10,10 +10,11 @@ from pyrogram.types import ( ) from classes.pyroclient import PyroClient +from modules import custom_filters @PyroClient.on_message( - ~filters.scheduled & filters.private & filters.command(["start"], prefixes=["/"]) # type: ignore + ~filters.scheduled & filters.private & filters.command(["start"], prefixes=["/"]) & ~custom_filters.context # type: ignore ) async def command_start(app: PyroClient, message: Message): user = await app.find_user(message.from_user) @@ -52,7 +53,9 @@ async def command_start(app: PyroClient, message: Message): ) while True: + app.contexts.append(message.from_user.id) answer = await listen_message(app, message.chat.id, 300) + app.contexts.remove(message.from_user.id) if answer is None or answer.text == "/cancel": await message.reply_text( diff --git a/plugins/commands/toggle.py b/plugins/commands/toggle.py index 91263e0..ac97f2b 100644 --- a/plugins/commands/toggle.py +++ b/plugins/commands/toggle.py @@ -4,10 +4,11 @@ from pyrogram import filters from pyrogram.types import Message from classes.pyroclient import PyroClient +from modules import custom_filters @PyroClient.on_message( - ~filters.scheduled & filters.private & filters.command(["toggle"], prefixes=["/"]) # type: ignore + ~filters.scheduled & filters.private & filters.command(["toggle"], prefixes=["/"]) & ~custom_filters.context # type: ignore ) async def command_toggle(app: PyroClient, message: Message): user = await app.find_user(message.from_user) diff --git a/plugins/commands/upcoming.py b/plugins/commands/upcoming.py index ac29e63..d468b0d 100644 --- a/plugins/commands/upcoming.py +++ b/plugins/commands/upcoming.py @@ -1,17 +1,17 @@ from datetime import datetime, timedelta, timezone -from pymongo.errors import OperationFailure from pyrogram import filters from pyrogram.types import Message from pytz import timezone as pytz_timezone from classes.garbage_entry import GarbageEntry from classes.pyroclient import PyroClient +from modules import custom_filters from modules.database import col_entries @PyroClient.on_message( - ~filters.scheduled & filters.private & filters.command(["upcoming"], prefixes=["/"]) # type: ignore + ~filters.scheduled & filters.private & filters.command(["upcoming"], prefixes=["/"]) & ~custom_filters.context # type: ignore ) async def command_upcoming(app: PyroClient, message: Message): user = await app.find_user(message.from_user) diff --git a/plugins/language.py b/plugins/language.py index 673b72b..54ac366 100644 --- a/plugins/language.py +++ b/plugins/language.py @@ -6,10 +6,11 @@ from pyrogram.types import CallbackQuery, Message from classes.callbacks import CallbackLanguage from classes.pyroclient import PyroClient +from modules import custom_filters @PyroClient.on_message( - ~filters.scheduled & filters.private & filters.command(["language"], prefixes=["/"]) # type: ignore + ~filters.scheduled & filters.private & filters.command(["language"], prefixes=["/"]) & ~custom_filters.context # type: ignore ) async def command_language(app: PyroClient, message: Message): user = await app.find_user(message.from_user) From 7447c6d232e8ef724be3ffa6a36003ab0cd228d0 Mon Sep 17 00:00:00 2001 From: Weblate Date: Wed, 30 Aug 2023 15:44:17 +0300 Subject: [PATCH 18/36] Translated using Weblate (German) Currently translated at 100.0% (0 of 0 strings) Added translation using Weblate (German) Co-authored-by: Anonymous Co-authored-by: Profitroll Co-authored-by: Weblate Translate-URL: https://weblate.end-play.xyz/projects/garbagereminder/telegrambot/de/ Translation: GarbageReminder/TelegramBot --- locale/de.json | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 locale/de.json diff --git a/locale/de.json b/locale/de.json new file mode 100644 index 0000000..1341a45 --- /dev/null +++ b/locale/de.json @@ -0,0 +1,98 @@ +{ + "metadata": { + "flag": "🇬🇧", + "name": "English", + "codes": [ + "en", + "en-GB" + ] + }, + "formats": { + "date": "%d/%m/%Y", + "time": "%H:%M" + }, + "garbage_types": { + "0": "🟤 Bio", + "1": "🟡 Plastic", + "2": "🔵 Paper", + "3": "⚫️ General", + "4": "🟢 Glass", + "5": "❓ Unspecified" + }, + "commands": { + "help": "Show help message", + "setup": "Select the location", + "toggle": "Enable/disable notifications", + "set_time": "Set notification time", + "set_offset": "Set notification days offset", + "upcoming": "Collection for the next 30 days", + "language": "Change bot's messages language", + "checkout": "Export or delete user data", + "import": "Upload from JSON to database", + "shutdown": "Turn off the bot", + "remove_commands": "Unregister all commands" + }, + "messages": { + "cancel": "Use /cancel if you want to cancel this operation.", + "cancelled": "Operation has been cancelled.", + "checkout_deleted": "🗑️ Your data has been deleted. If you want to start using this bot again, please use /setup command. Otherwise delete/block the bot and do not interact with it anymore.", + "checkout_deletion": "Alright. Please, confirm that you want to delete your data from the bot.\n\nFollowing data will be deleted:\n• Selected location\n• Preferred language of all messages\n• Time of the notifications\n• Offset of the notifications\n\nUse keyboard provided to confirm and continue or /cancel to abort this operation.", + "checkout": "Here's pretty much all the data bot has. Please, use these buttons to choose whether you want to delete your data from the bot.", + "commands_removed": "✅ All currently registered commands have been unregistered. Commands will be registered again on bot's start.", + "help": "🔔 This bot sends you notifications about garbage collection according to your local schedule.\n\n**Available commands**\n/help - Show this message\n/setup - Select the location\n/toggle - Disable/enable the reminders\n/set_time - Set the reminders' time\n/set_offset - Set offset between reminders and collection\n/upcoming - Show the upcoming collection\n/language - Select the bot's language\n/checkout - Export or remove your data\n\n💭 You can also suggest adding your town/district to the bot by contacting the admins using [this link]({url_contact}) and providing your schedule.\n\n⚙️ Want to host this bot yourself or make some changes? It's open-source, so you can basically fork it. Take a look at [bot's repository]({url_repo}) for details.\n\nHappy using! 🤗", + "import_finished": "You have successfully inserted {count} entries.", + "import_invalid_date": "Entries contain invalid date formats. Use **ISO 8601** date format.", + "import_invalid_filetype": "Invalid input. Please, send me a JSON file with entries. {cancel_notice}", + "import_invalid": "This is not a valid garbage collection JSON.", + "import": "Alright. Send me a valid JSON.", + "locale_choice": "Alright. Please choose the language using keyboard below.", + "location_name_empty": "Could not find any locations by this name. Try rephrasing it or make sure you use the same location language and name itself as it in written by your local authorities in garbage collection schedule.\n\n{cancel_notice}", + "location_name_invalid": "Please, send the name of the location as a text. {cancel_notice}", + "location_name": "Please, send me a location name. It should be the name used in your local authorities' garbage collection schedule. This usually is a name of the district or even the town itself.", + "location_select": "Select the location using the keyboard provided.", + "reminder": "**Garbage Collection**\n\nType: {type}\nDate: {date}\n\nDon't forget to prepare your bin for collection!", + "search_nearby_empty": "Could not find any locations nearby. Let's try using the name search.", + "selection_invalid": "Please, select a valid option using the keyboard provided. {cancel_notice}", + "set_offset_finished": "🔔 Notifications offset has been updated! You will now receive notification about collection **{offset} d.** before the collection at {time}. {toggle_notice}", + "set_offset_invalid": "Please, provide a valid integer number of days in range 0 to 7 (inclusive). {cancel_notice}", + "set_offset": "Alright. Please, send how many days in advance do you want to get a notification about the collection.", + "set_time_finished": "🔔 Notifications time has been updated! You will now receive notification about collection {offset} d. before the collection at **{time}**. {toggle_notice}", + "set_time_invalid": "Please, provide a valid time in HH:MM format. {cancel_notice}", + "set_time": "Alright. Please, send your desired time in HH:MM format.", + "setup_finished": "✅ Finished! Your location is now **{name}**. You will receive the notifications about garbage collection {offset} d. in advance at {time}.", + "setup_retry": "ℹ️ If you want try selecting the location again, use the /setup command.", + "setup": "⚙️ Let's begin configuration with the search for your location.\n\nPlease, select whether you want to search among the locations near you or go straight to the search by location name.\n\nNote that the location you send will **NOT** be saved anywhere and is only used for location lookup in the database.", + "start_code_invalid": "🚫 You have started the bot by the link containing a location, but it does not seem to be a valid one. Please, use the command /setup to manually configure the location.", + "start_code": "ℹ️ You have started the bot by the link containing a location **{name}**.\n\nPlease, confirm whether you want to use it as your location.", + "start_configure": "📍 Let's configure your location. Press the button on pop-up keyboard to start the process.", + "start_selection_no": "Alright, you're on your own now. Please, use the command /setup to configure your location and start receiving reminders.", + "start_selection_yes": "✅ Finished! Your location is now **{name}**. You will receive reminders about garbage collection {offset} d. in advance at {time}.\n\nPlease, visit /help if you want to know how to change notifications time or disable them.", + "start": "👋 Welcome!\n\nThis small open-source bot is made to simplify your life a bit easier by sending you notifications about upcoming garbage collection in your location.\n\nBy using this bot you accept [Privacy Policy]({privacy_policy}), otherwise please block and remove this bot before further interaction.\n\nNow the official part is over so you can dive into the bot.", + "toggle_disabled": "🔕 Notifications have been disabled.", + "toggle_enabled_location": "🔔 Notifications have been enabled {offset} d. before garbage collection at {time} at the **{name}**.", + "toggle_enabled": "🔔 Notifications have been enabled {offset} d. before garbage collection at {time}. Use /setup to select your location.", + "toggle": "Execute /toggle to enable notifications.", + "upcoming_empty_location": "You have no location set. Use /setup to select your location.", + "upcoming_empty": "No garbage collection entries found for the next 30 days at **{name}**", + "upcoming": "Upcoming garbage collection:\n\n{entries}" + }, + "force_replies": { + "import": "JSON with garbage entries", + "location_name": "Location name", + "set_offset": "Number of days", + "set_time": "Time as HH:MM" + }, + "buttons": { + "delete_confirm": "I agree and want to proceed", + "delete_no": "❌ No, I don't want to delete it", + "delete_yes": "✅ Yes, I want to delete it", + "search_name": "🔎 Search by location name", + "search_nearby": "📍 Search nearby locations", + "start_code_no": "❌ No, I don't want to use it", + "start_code_yes": "✅ Yes, I want to use it", + "start_configure": "⚙️ Let's configure the bot" + }, + "callbacks": { + "locale_set": "Your language now is: {locale}" + } +} From cca3101a4ae6609ef66cf5125d063a4b0acf10b1 Mon Sep 17 00:00:00 2001 From: Weblate Date: Wed, 30 Aug 2023 15:47:56 +0300 Subject: [PATCH 19/36] Translated using Weblate (German) Currently translated at 14.1% (11 of 78 strings) Co-authored-by: Profitroll Translate-URL: https://weblate.end-play.xyz/projects/garbagereminder/telegrambot/de/ Translation: GarbageReminder/TelegramBot --- locale/de.json | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/locale/de.json b/locale/de.json index 1341a45..bed13a5 100644 --- a/locale/de.json +++ b/locale/de.json @@ -1,23 +1,23 @@ { "metadata": { - "flag": "🇬🇧", - "name": "English", + "flag": "🇩🇪", + "name": "Deutsch", "codes": [ - "en", - "en-GB" + "de", + "de-DE" ] }, "formats": { - "date": "%d/%m/%Y", + "date": "%d.%m.%Y", "time": "%H:%M" }, "garbage_types": { - "0": "🟤 Bio", - "1": "🟡 Plastic", - "2": "🔵 Paper", - "3": "⚫️ General", - "4": "🟢 Glass", - "5": "❓ Unspecified" + "0": "🟤 Biotonne", + "1": "🟡 Gelber Sack", + "2": "🔵 Papiertonne", + "3": "⚫️ Restmüll", + "4": "🟢 Altglas", + "5": "❓ Unbestimmt" }, "commands": { "help": "Show help message", From d304dc3235d245c768014958abf2e09f6b93e2c3 Mon Sep 17 00:00:00 2001 From: Weblate Date: Wed, 30 Aug 2023 15:47:56 +0300 Subject: [PATCH 20/36] Translated using Weblate (Russian) Currently translated at 100.0% (0 of 0 strings) Added translation using Weblate (Russian) Co-authored-by: Anonymous Co-authored-by: Profitroll Co-authored-by: Weblate Translate-URL: https://weblate.end-play.xyz/projects/garbagereminder/telegrambot/ru/ Translation: GarbageReminder/TelegramBot --- locale/ru.json | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 locale/ru.json diff --git a/locale/ru.json b/locale/ru.json new file mode 100644 index 0000000..821c367 --- /dev/null +++ b/locale/ru.json @@ -0,0 +1,98 @@ +{ + "metadata": { + "codes": [ + "en", + "en-GB" + ], + "flag": "🇬🇧", + "name": "English" + }, + "messages": { + "cancelled": "Operation has been cancelled.", + "setup": "⚙️ Let's begin configuration with the search for your location.\n\nPlease, select whether you want to search among the locations near you or go straight to the search by location name.\n\nNote that the location you send will **NOT** be saved anywhere and is only used for location lookup in the database.", + "cancel": "Use /cancel if you want to cancel this operation.", + "checkout_deleted": "🗑️ Your data has been deleted. If you want to start using this bot again, please use /setup command. Otherwise delete/block the bot and do not interact with it anymore.", + "checkout_deletion": "Alright. Please, confirm that you want to delete your data from the bot.\n\nFollowing data will be deleted:\n• Selected location\n• Preferred language of all messages\n• Time of the notifications\n• Offset of the notifications\n\nUse keyboard provided to confirm and continue or /cancel to abort this operation.", + "checkout": "Here's pretty much all the data bot has. Please, use these buttons to choose whether you want to delete your data from the bot.", + "commands_removed": "✅ All currently registered commands have been unregistered. Commands will be registered again on bot's start.", + "help": "🔔 This bot sends you notifications about garbage collection according to your local schedule.\n\n**Available commands**\n/help - Show this message\n/setup - Select the location\n/toggle - Disable/enable the reminders\n/set_time - Set the reminders' time\n/set_offset - Set offset between reminders and collection\n/upcoming - Show the upcoming collection\n/language - Select the bot's language\n/checkout - Export or remove your data\n\n💭 You can also suggest adding your town/district to the bot by contacting the admins using [this link]({url_contact}) and providing your schedule.\n\n⚙️ Want to host this bot yourself or make some changes? It's open-source, so you can basically fork it. Take a look at [bot's repository]({url_repo}) for details.\n\nHappy using! 🤗", + "import_finished": "You have successfully inserted {count} entries.", + "import_invalid_date": "Entries contain invalid date formats. Use **ISO 8601** date format.", + "import_invalid_filetype": "Invalid input. Please, send me a JSON file with entries. {cancel_notice}", + "import_invalid": "This is not a valid garbage collection JSON.", + "import": "Alright. Send me a valid JSON.", + "locale_choice": "Alright. Please choose the language using keyboard below.", + "location_name_empty": "Could not find any locations by this name. Try rephrasing it or make sure you use the same location language and name itself as it in written by your local authorities in garbage collection schedule.\n\n{cancel_notice}", + "location_name_invalid": "Please, send the name of the location as a text. {cancel_notice}", + "location_name": "Please, send me a location name. It should be the name used in your local authorities' garbage collection schedule. This usually is a name of the district or even the town itself.", + "location_select": "Select the location using the keyboard provided.", + "reminder": "**Garbage Collection**\n\nType: {type}\nDate: {date}\n\nDon't forget to prepare your bin for collection!", + "search_nearby_empty": "Could not find any locations nearby. Let's try using the name search.", + "selection_invalid": "Please, select a valid option using the keyboard provided. {cancel_notice}", + "set_offset_finished": "🔔 Notifications offset has been updated! You will now receive notification about collection **{offset} d.** before the collection at {time}. {toggle_notice}", + "set_offset_invalid": "Please, provide a valid integer number of days in range 0 to 7 (inclusive). {cancel_notice}", + "set_offset": "Alright. Please, send how many days in advance do you want to get a notification about the collection.", + "set_time_finished": "🔔 Notifications time has been updated! You will now receive notification about collection {offset} d. before the collection at **{time}**. {toggle_notice}", + "set_time_invalid": "Please, provide a valid time in HH:MM format. {cancel_notice}", + "set_time": "Alright. Please, send your desired time in HH:MM format.", + "setup_finished": "✅ Finished! Your location is now **{name}**. You will receive the notifications about garbage collection {offset} d. in advance at {time}.", + "setup_retry": "ℹ️ If you want try selecting the location again, use the /setup command.", + "start_code_invalid": "🚫 You have started the bot by the link containing a location, but it does not seem to be a valid one. Please, use the command /setup to manually configure the location.", + "start_code": "ℹ️ You have started the bot by the link containing a location **{name}**.\n\nPlease, confirm whether you want to use it as your location.", + "start_configure": "📍 Let's configure your location. Press the button on pop-up keyboard to start the process.", + "start_selection_no": "Alright, you're on your own now. Please, use the command /setup to configure your location and start receiving reminders.", + "start_selection_yes": "✅ Finished! Your location is now **{name}**. You will receive reminders about garbage collection {offset} d. in advance at {time}.\n\nPlease, visit /help if you want to know how to change notifications time or disable them.", + "start": "👋 Welcome!\n\nThis small open-source bot is made to simplify your life a bit easier by sending you notifications about upcoming garbage collection in your location.\n\nBy using this bot you accept [Privacy Policy]({privacy_policy}), otherwise please block and remove this bot before further interaction.\n\nNow the official part is over so you can dive into the bot.", + "toggle_disabled": "🔕 Notifications have been disabled.", + "toggle_enabled_location": "🔔 Notifications have been enabled {offset} d. before garbage collection at {time} at the **{name}**.", + "toggle_enabled": "🔔 Notifications have been enabled {offset} d. before garbage collection at {time}. Use /setup to select your location.", + "toggle": "Execute /toggle to enable notifications.", + "upcoming_empty_location": "You have no location set. Use /setup to select your location.", + "upcoming_empty": "No garbage collection entries found for the next 30 days at **{name}**", + "upcoming": "Upcoming garbage collection:\n\n{entries}" + }, + "formats": { + "date": "%d/%m/%Y", + "time": "%H:%M" + }, + "garbage_types": { + "0": "🟤 Bio", + "1": "🟡 Plastic", + "2": "🔵 Paper", + "3": "⚫️ General", + "4": "🟢 Glass", + "5": "❓ Unspecified" + }, + "commands": { + "help": "Show help message", + "setup": "Select the location", + "toggle": "Enable/disable notifications", + "set_time": "Set notification time", + "set_offset": "Set notification days offset", + "upcoming": "Collection for the next 30 days", + "language": "Change bot's messages language", + "checkout": "Export or delete user data", + "import": "Upload from JSON to database", + "shutdown": "Turn off the bot", + "remove_commands": "Unregister all commands" + }, + "buttons": { + "delete_confirm": "I agree and want to proceed", + "delete_no": "❌ No, I don't want to delete it", + "delete_yes": "✅ Yes, I want to delete it", + "search_name": "🔎 Search by location name", + "search_nearby": "📍 Search nearby locations", + "start_code_no": "❌ No, I don't want to use it", + "start_code_yes": "✅ Yes, I want to use it", + "start_configure": "⚙️ Let's configure the bot" + }, + "callbacks": { + "locale_set": "Your language now is: {locale}" + }, + "force_replies": { + "import": "JSON with garbage entries", + "location_name": "Location name", + "set_offset": "Number of days", + "set_time": "Time as HH:MM" + } +} From 98b1f57067ef0f95f35fa893df4c38364cb4969a Mon Sep 17 00:00:00 2001 From: Weblate Date: Wed, 30 Aug 2023 15:49:34 +0300 Subject: [PATCH 21/36] Translated using Weblate (Russian) Currently translated at 100.0% (78 of 78 strings) Co-authored-by: Profitroll Translate-URL: https://weblate.end-play.xyz/projects/garbagereminder/telegrambot/ru/ Translation: GarbageReminder/TelegramBot --- locale/ru.json | 154 ++++++++++++++++++++++++------------------------- 1 file changed, 77 insertions(+), 77 deletions(-) diff --git a/locale/ru.json b/locale/ru.json index 821c367..5f1168f 100644 --- a/locale/ru.json +++ b/locale/ru.json @@ -1,98 +1,98 @@ { "metadata": { "codes": [ - "en", - "en-GB" + "ru", + "ru-RU" ], - "flag": "🇬🇧", - "name": "English" + "flag": "🇺🇦", + "name": "Російська" }, "messages": { - "cancelled": "Operation has been cancelled.", - "setup": "⚙️ Let's begin configuration with the search for your location.\n\nPlease, select whether you want to search among the locations near you or go straight to the search by location name.\n\nNote that the location you send will **NOT** be saved anywhere and is only used for location lookup in the database.", - "cancel": "Use /cancel if you want to cancel this operation.", - "checkout_deleted": "🗑️ Your data has been deleted. If you want to start using this bot again, please use /setup command. Otherwise delete/block the bot and do not interact with it anymore.", - "checkout_deletion": "Alright. Please, confirm that you want to delete your data from the bot.\n\nFollowing data will be deleted:\n• Selected location\n• Preferred language of all messages\n• Time of the notifications\n• Offset of the notifications\n\nUse keyboard provided to confirm and continue or /cancel to abort this operation.", - "checkout": "Here's pretty much all the data bot has. Please, use these buttons to choose whether you want to delete your data from the bot.", - "commands_removed": "✅ All currently registered commands have been unregistered. Commands will be registered again on bot's start.", - "help": "🔔 This bot sends you notifications about garbage collection according to your local schedule.\n\n**Available commands**\n/help - Show this message\n/setup - Select the location\n/toggle - Disable/enable the reminders\n/set_time - Set the reminders' time\n/set_offset - Set offset between reminders and collection\n/upcoming - Show the upcoming collection\n/language - Select the bot's language\n/checkout - Export or remove your data\n\n💭 You can also suggest adding your town/district to the bot by contacting the admins using [this link]({url_contact}) and providing your schedule.\n\n⚙️ Want to host this bot yourself or make some changes? It's open-source, so you can basically fork it. Take a look at [bot's repository]({url_repo}) for details.\n\nHappy using! 🤗", - "import_finished": "You have successfully inserted {count} entries.", - "import_invalid_date": "Entries contain invalid date formats. Use **ISO 8601** date format.", - "import_invalid_filetype": "Invalid input. Please, send me a JSON file with entries. {cancel_notice}", - "import_invalid": "This is not a valid garbage collection JSON.", - "import": "Alright. Send me a valid JSON.", - "locale_choice": "Alright. Please choose the language using keyboard below.", - "location_name_empty": "Could not find any locations by this name. Try rephrasing it or make sure you use the same location language and name itself as it in written by your local authorities in garbage collection schedule.\n\n{cancel_notice}", - "location_name_invalid": "Please, send the name of the location as a text. {cancel_notice}", - "location_name": "Please, send me a location name. It should be the name used in your local authorities' garbage collection schedule. This usually is a name of the district or even the town itself.", - "location_select": "Select the location using the keyboard provided.", - "reminder": "**Garbage Collection**\n\nType: {type}\nDate: {date}\n\nDon't forget to prepare your bin for collection!", - "search_nearby_empty": "Could not find any locations nearby. Let's try using the name search.", - "selection_invalid": "Please, select a valid option using the keyboard provided. {cancel_notice}", - "set_offset_finished": "🔔 Notifications offset has been updated! You will now receive notification about collection **{offset} d.** before the collection at {time}. {toggle_notice}", - "set_offset_invalid": "Please, provide a valid integer number of days in range 0 to 7 (inclusive). {cancel_notice}", - "set_offset": "Alright. Please, send how many days in advance do you want to get a notification about the collection.", - "set_time_finished": "🔔 Notifications time has been updated! You will now receive notification about collection {offset} d. before the collection at **{time}**. {toggle_notice}", - "set_time_invalid": "Please, provide a valid time in HH:MM format. {cancel_notice}", - "set_time": "Alright. Please, send your desired time in HH:MM format.", - "setup_finished": "✅ Finished! Your location is now **{name}**. You will receive the notifications about garbage collection {offset} d. in advance at {time}.", - "setup_retry": "ℹ️ If you want try selecting the location again, use the /setup command.", - "start_code_invalid": "🚫 You have started the bot by the link containing a location, but it does not seem to be a valid one. Please, use the command /setup to manually configure the location.", - "start_code": "ℹ️ You have started the bot by the link containing a location **{name}**.\n\nPlease, confirm whether you want to use it as your location.", - "start_configure": "📍 Let's configure your location. Press the button on pop-up keyboard to start the process.", - "start_selection_no": "Alright, you're on your own now. Please, use the command /setup to configure your location and start receiving reminders.", - "start_selection_yes": "✅ Finished! Your location is now **{name}**. You will receive reminders about garbage collection {offset} d. in advance at {time}.\n\nPlease, visit /help if you want to know how to change notifications time or disable them.", - "start": "👋 Welcome!\n\nThis small open-source bot is made to simplify your life a bit easier by sending you notifications about upcoming garbage collection in your location.\n\nBy using this bot you accept [Privacy Policy]({privacy_policy}), otherwise please block and remove this bot before further interaction.\n\nNow the official part is over so you can dive into the bot.", - "toggle_disabled": "🔕 Notifications have been disabled.", - "toggle_enabled_location": "🔔 Notifications have been enabled {offset} d. before garbage collection at {time} at the **{name}**.", - "toggle_enabled": "🔔 Notifications have been enabled {offset} d. before garbage collection at {time}. Use /setup to select your location.", - "toggle": "Execute /toggle to enable notifications.", - "upcoming_empty_location": "You have no location set. Use /setup to select your location.", - "upcoming_empty": "No garbage collection entries found for the next 30 days at **{name}**", - "upcoming": "Upcoming garbage collection:\n\n{entries}" + "cancelled": "Операцію скасовано.", + "setup": "⚙️ Почнемо налаштування з пошуку Вашого місцезнаходження.\n\nБудь ласка, виберіть, чи хочете Ви шукати серед найближчих до Вас локацій, чи одразу перейти до пошуку за назвою.\n\nЗверніть увагу, що надіслане Вами місцезнаходження **НЕ** зберігається ніде і використовується лише для пошуку місць поряд в базі даних.", + "cancel": "Якщо Ви хочете скасувати цю операцію, використовуйте /cancel.", + "checkout_deleted": "🗑️ Ваші дані було видалено. Якщо Ви хочете знову почати користуватися цим ботом, скористайтеся командою /setup. В іншому випадку видаліть/заблокуйте бота і більше не взаємодійте з ним.", + "checkout_deletion": "Гаразд. Будь ласка, підтвердіть, що Ви хочете видалити свої дані з бота.\n\nНаступні дані будуть видалені:\n• Вибране місце\n• Бажана мова всіх повідомлень\n• Час сповіщень\n• Зсув сповіщень\n\nВикористовуйте клавіатуру, щоб підтвердити й продовжити або /cancel, щоб перервати цю операцію.", + "checkout": "Це фактично всі дані, які має бот. Будь ласка, використовуйте ці кнопки, щоб вибрати, чи хочете Ви видалити свої дані з бота.", + "commands_removed": "✅ Всі зареєстровані на цю мить команди було видалено. Команди будуть зареєстровані знову при запуску бота.", + "help": "🔔 Цей бот надсилає сповіщення про вивезення сміття згідно з Вашим місцевим графіком.\n\n**Доступні команди**\n/help - Показати це повідомлення\n/setup - Вибрати місце розташування\n/toggle - Увімкнути/вимкнути нагадування\n/set_time - Встановити час нагадувань\n/set_offset - Встановити зсув між нагадуваннями та вивозом\n/upcoming - Показати майбутні вивезення\n/language - Обрати мову бота\n/checkout - Експортувати або видалити дані\n\nВи також можете запропонувати додати своє місто/район до бота, зв'язавшись з адміністраторами за [цим посиланням]({url_contact}) та вказавши свій розклад.\n\n⚙️ Бажаєте розмістити цього бота самостійно або внести деякі зміни? Бот має відкритий вихідний код, тож ви можете форкнути його. Ознайомтесь із [репозиторієм бота]({url_repo}) щоб дізнатись деталі.\n\nПриємного користування! 🤗", + "import_finished": "Ви успішно вставили {count} записів.", + "import_invalid_date": "Записи містять невірні формати дат. Використовуйте формат дати **ISO 8601**.", + "import_invalid_filetype": "Неправильний ввід. Будь ласка, надішліть мені JSON-файл із записами. {cancel_notice}", + "import_invalid": "Це недійсний JSON даних збору сміття.", + "import": "Гаразд. Надішліть правильний JSON.", + "locale_choice": "Гаразд. Будь ласка, оберіть мову за допомогою клавіатури нижче.", + "location_name_empty": "Не вдалося знайти жодного населеного пункту з такою назвою. Спробуйте перефразувати назву або переконайтеся, що Ви використовуєте ту саму мову та назву, що й місцева влада у графіку вивезення сміття.\n\n{cancel_notice}", + "location_name_invalid": "Будь ласка, надішліть назву місця у вигляді тексту. {cancel_notice}", + "location_name": "Будь ласка, надішліть мені назву населеного пункту. Це має бути назва, яка використовується у графіку вивезення сміття Вашою місцевою владою. Зазвичай це назва району або міста.", + "location_select": "Виберіть місце за допомогою показаної клавіатури.", + "reminder": "**Вивіз сміття**\n\nТип: {type}\nДата: {date}\n\nНе забудьте підготувати свій контейнер до збору!", + "search_nearby_empty": "Не вдалося знайти жодної локації поблизу. Спробуємо скористатися пошуком за назвою.", + "selection_invalid": "Будь ласка, виберіть правильний варіант за допомогою клавіатури. {cancel_notice}", + "set_offset_finished": "🔔 Час сповіщень було оновлено! Тепер Ви будете отримувати сповіщення про вивіз сміття за **{offset} д.** до вивозу о {time}. {toggle_notice}", + "set_offset_invalid": "Будь ласка, вкажіть дійсну цілу кількість днів у діапазоні від 0 до 7 (включно). {cancel_notice}", + "set_offset": "Гаразд. Будь ласка, напишіть, за скільки днів до збору Ви хочете отримати сповіщення про збір.", + "set_time_finished": "🔔 Час сповіщень було оновлено! Тепер Ви будете отримувати сповіщення про вивіз сміття за {offset} д. до вивозу о **{time}**. {toggle_notice}", + "set_time_invalid": "Будь ласка, вкажіть дійсний час у форматі ГГ:ХХ. {cancel_notice}", + "set_time": "Гаразд. Будь ласка, надішліть бажаний час у форматі ГГ:ХХ.", + "setup_finished": "✅ Готово! Ваше місцезнаходження тепер **{name}**. Ви будете отримувати сповіщення про вивезення сміття за {offset} д. заздалегідь о {time}.", + "setup_retry": "ℹ️ Якщо Ви захочете вибрати місце розташування, скористайтеся командою /setup.", + "start_code_invalid": "🚫 Ви запустили бота за посиланням, що містить локацію, але, схоже, вона не є дійсною. Будь ласка, скористайтеся командою /setup, щоб налаштувати локацію вручну.", + "start_code": "ℹ️ Ви запустили бота за посиланням, що містить локацію **{name}**.\n\nБудь ласка, підтвердіть, чи хочете Ви використовувати її як свою локацію для сповіщень.", + "start_configure": "📍 Налаштуймо Вашу локацію. Натисніть кнопку на показаній клавіатурі, щоб почати процес.", + "start_selection_no": "Гаразд, тепер Ви самі по собі. Будь ласка, скористайтеся командою /setup, щоб налаштувати своє місцезнаходження і почати отримувати нагадування.", + "start_selection_yes": "✅ Готово! Ваша локація тепер **{name}**. Ви будете отримувати нагадування про вивезення сміття за {offset} д. заздалегідь о {time}.\n\nБудь ласка, скористайтесь /help, якщо Ви хочете дізнатися, як змінити час сповіщень або вимкнути їх.", + "start": "👋 Вітання!\n\nЦей невеличкий бот з відкритим вихідним кодом створений для того, щоб трохи спростити Вам життя, надсилаючи сповіщення про вивіз сміття у вашому регіоні.\n\nКористуючись цим ботом, Ви приймаєте [Політику конфіденційності]({privacy_policy}), в іншому випадку, будь ласка, заблокуйте та видаліть цього бота перед подальшою взаємодією.\n\nТепер офіційна частина закінчена, тож Ви можете зануритися в бота.", + "toggle_disabled": "🔕 Сповіщення було вимкнено.", + "toggle_enabled_location": "🔔 Сповіщення увімкнено за {offset} д. до вивезення сміття о {time} для локації **{name}**.", + "toggle_enabled": "🔔 Сповіщення було увімкнено за {offset} д. до вивезення сміття о {time}. Оберіть своє розташування за допомогою /setup.", + "toggle": "Використовуйте /toggle, щоб увімкнути сповіщення.", + "upcoming_empty_location": "У Вас не встановлено локацію вивозу. Оберіть свою локацію за допомогою /setup.", + "upcoming_empty": "Не знайдено записів про вивезення сміття на найближчі 30 днів для **{name}**", + "upcoming": "Найближчі вивози сміття:\n\n{entries}" }, "formats": { - "date": "%d/%m/%Y", + "date": "%d.%m.%Y", "time": "%H:%M" }, "garbage_types": { - "0": "🟤 Bio", - "1": "🟡 Plastic", - "2": "🔵 Paper", - "3": "⚫️ General", - "4": "🟢 Glass", - "5": "❓ Unspecified" + "0": "🟤 Біо", + "1": "🟡 Пластик", + "2": "🔵 Папір", + "3": "⚫️ Загальне", + "4": "🟢 Скло", + "5": "❓Невизначене" }, "commands": { - "help": "Show help message", - "setup": "Select the location", - "toggle": "Enable/disable notifications", - "set_time": "Set notification time", - "set_offset": "Set notification days offset", - "upcoming": "Collection for the next 30 days", - "language": "Change bot's messages language", - "checkout": "Export or delete user data", - "import": "Upload from JSON to database", - "shutdown": "Turn off the bot", - "remove_commands": "Unregister all commands" + "help": "Показати меню допомоги", + "setup": "Обрати місце розташування", + "toggle": "Увімкнути/вимкнути сповіщення", + "set_time": "Встановити час сповіщень", + "set_offset": "Встановити зміщення сповіщень", + "upcoming": "Дати збору на наступні 30 днів", + "language": "Змінити мову повідомлень бота", + "checkout": "Експортувати або видалити дані", + "import": "Завантажити з JSON до бази даних", + "shutdown": "Вимкнути бота", + "remove_commands": "Видалити всі команди" }, "buttons": { - "delete_confirm": "I agree and want to proceed", - "delete_no": "❌ No, I don't want to delete it", - "delete_yes": "✅ Yes, I want to delete it", - "search_name": "🔎 Search by location name", - "search_nearby": "📍 Search nearby locations", - "start_code_no": "❌ No, I don't want to use it", - "start_code_yes": "✅ Yes, I want to use it", - "start_configure": "⚙️ Let's configure the bot" + "delete_confirm": "Я погоджуюсь і хочу продовжити", + "delete_no": "❌ Ні, я не хочу видаляти їх", + "delete_yes": "✅ Так, я хочу видалити їх", + "search_name": "🔎 Пошук за назвою місця", + "search_nearby": "📍 Пошук найближчих локацій", + "start_code_no": "❌ Ні, я не хочу її використовувати", + "start_code_yes": "✅ Так, я хочу її використовувати", + "start_configure": "⚙️ Налаштуймо бота" }, "callbacks": { - "locale_set": "Your language now is: {locale}" + "locale_set": "Встановлено мову: {locale}" }, "force_replies": { - "import": "JSON with garbage entries", - "location_name": "Location name", - "set_offset": "Number of days", - "set_time": "Time as HH:MM" + "import": "JSON із записами вивозу сміття", + "location_name": "Назва локації", + "set_offset": "Кількість днів", + "set_time": "Час у вигляді ГГ:ХХ" } } From 6bc3c0ab703a90930f9881e2b1dc0d9a211d5732 Mon Sep 17 00:00:00 2001 From: Weblate Date: Wed, 30 Aug 2023 16:11:20 +0300 Subject: [PATCH 22/36] Translated using Weblate (German) Currently translated at 100.0% (78 of 78 strings) Co-authored-by: Profitroll Translate-URL: https://weblate.end-play.xyz/projects/garbagereminder/telegrambot/de/ Translation: GarbageReminder/TelegramBot --- locale/de.json | 132 ++++++++++++++++++++++++------------------------- 1 file changed, 66 insertions(+), 66 deletions(-) diff --git a/locale/de.json b/locale/de.json index bed13a5..708b858 100644 --- a/locale/de.json +++ b/locale/de.json @@ -20,79 +20,79 @@ "5": "❓ Unbestimmt" }, "commands": { - "help": "Show help message", - "setup": "Select the location", - "toggle": "Enable/disable notifications", - "set_time": "Set notification time", - "set_offset": "Set notification days offset", - "upcoming": "Collection for the next 30 days", - "language": "Change bot's messages language", - "checkout": "Export or delete user data", - "import": "Upload from JSON to database", - "shutdown": "Turn off the bot", - "remove_commands": "Unregister all commands" + "help": "Hilfemenü anzeigen", + "setup": "Standort auswählen", + "toggle": "Benachrichtigungen aktivieren/deaktivieren", + "set_time": "Benachrichtigungszeit einstellen", + "set_offset": "Benachrichtigungstage-Offset festlegen", + "upcoming": "Sammlung für die nächsten 30 Tage", + "language": "Sprache der Botnachrichten ändern", + "checkout": "Benutzerdaten exportieren oder löschen", + "import": "Hochladen von JSON in die Datenbank", + "shutdown": "Bot ausschalten", + "remove_commands": "Alle Kommandos abmelden" }, "messages": { - "cancel": "Use /cancel if you want to cancel this operation.", - "cancelled": "Operation has been cancelled.", - "checkout_deleted": "🗑️ Your data has been deleted. If you want to start using this bot again, please use /setup command. Otherwise delete/block the bot and do not interact with it anymore.", - "checkout_deletion": "Alright. Please, confirm that you want to delete your data from the bot.\n\nFollowing data will be deleted:\n• Selected location\n• Preferred language of all messages\n• Time of the notifications\n• Offset of the notifications\n\nUse keyboard provided to confirm and continue or /cancel to abort this operation.", - "checkout": "Here's pretty much all the data bot has. Please, use these buttons to choose whether you want to delete your data from the bot.", - "commands_removed": "✅ All currently registered commands have been unregistered. Commands will be registered again on bot's start.", - "help": "🔔 This bot sends you notifications about garbage collection according to your local schedule.\n\n**Available commands**\n/help - Show this message\n/setup - Select the location\n/toggle - Disable/enable the reminders\n/set_time - Set the reminders' time\n/set_offset - Set offset between reminders and collection\n/upcoming - Show the upcoming collection\n/language - Select the bot's language\n/checkout - Export or remove your data\n\n💭 You can also suggest adding your town/district to the bot by contacting the admins using [this link]({url_contact}) and providing your schedule.\n\n⚙️ Want to host this bot yourself or make some changes? It's open-source, so you can basically fork it. Take a look at [bot's repository]({url_repo}) for details.\n\nHappy using! 🤗", - "import_finished": "You have successfully inserted {count} entries.", - "import_invalid_date": "Entries contain invalid date formats. Use **ISO 8601** date format.", - "import_invalid_filetype": "Invalid input. Please, send me a JSON file with entries. {cancel_notice}", - "import_invalid": "This is not a valid garbage collection JSON.", - "import": "Alright. Send me a valid JSON.", - "locale_choice": "Alright. Please choose the language using keyboard below.", - "location_name_empty": "Could not find any locations by this name. Try rephrasing it or make sure you use the same location language and name itself as it in written by your local authorities in garbage collection schedule.\n\n{cancel_notice}", - "location_name_invalid": "Please, send the name of the location as a text. {cancel_notice}", - "location_name": "Please, send me a location name. It should be the name used in your local authorities' garbage collection schedule. This usually is a name of the district or even the town itself.", - "location_select": "Select the location using the keyboard provided.", - "reminder": "**Garbage Collection**\n\nType: {type}\nDate: {date}\n\nDon't forget to prepare your bin for collection!", - "search_nearby_empty": "Could not find any locations nearby. Let's try using the name search.", - "selection_invalid": "Please, select a valid option using the keyboard provided. {cancel_notice}", - "set_offset_finished": "🔔 Notifications offset has been updated! You will now receive notification about collection **{offset} d.** before the collection at {time}. {toggle_notice}", - "set_offset_invalid": "Please, provide a valid integer number of days in range 0 to 7 (inclusive). {cancel_notice}", - "set_offset": "Alright. Please, send how many days in advance do you want to get a notification about the collection.", - "set_time_finished": "🔔 Notifications time has been updated! You will now receive notification about collection {offset} d. before the collection at **{time}**. {toggle_notice}", - "set_time_invalid": "Please, provide a valid time in HH:MM format. {cancel_notice}", - "set_time": "Alright. Please, send your desired time in HH:MM format.", - "setup_finished": "✅ Finished! Your location is now **{name}**. You will receive the notifications about garbage collection {offset} d. in advance at {time}.", - "setup_retry": "ℹ️ If you want try selecting the location again, use the /setup command.", - "setup": "⚙️ Let's begin configuration with the search for your location.\n\nPlease, select whether you want to search among the locations near you or go straight to the search by location name.\n\nNote that the location you send will **NOT** be saved anywhere and is only used for location lookup in the database.", - "start_code_invalid": "🚫 You have started the bot by the link containing a location, but it does not seem to be a valid one. Please, use the command /setup to manually configure the location.", - "start_code": "ℹ️ You have started the bot by the link containing a location **{name}**.\n\nPlease, confirm whether you want to use it as your location.", - "start_configure": "📍 Let's configure your location. Press the button on pop-up keyboard to start the process.", - "start_selection_no": "Alright, you're on your own now. Please, use the command /setup to configure your location and start receiving reminders.", - "start_selection_yes": "✅ Finished! Your location is now **{name}**. You will receive reminders about garbage collection {offset} d. in advance at {time}.\n\nPlease, visit /help if you want to know how to change notifications time or disable them.", - "start": "👋 Welcome!\n\nThis small open-source bot is made to simplify your life a bit easier by sending you notifications about upcoming garbage collection in your location.\n\nBy using this bot you accept [Privacy Policy]({privacy_policy}), otherwise please block and remove this bot before further interaction.\n\nNow the official part is over so you can dive into the bot.", - "toggle_disabled": "🔕 Notifications have been disabled.", - "toggle_enabled_location": "🔔 Notifications have been enabled {offset} d. before garbage collection at {time} at the **{name}**.", - "toggle_enabled": "🔔 Notifications have been enabled {offset} d. before garbage collection at {time}. Use /setup to select your location.", - "toggle": "Execute /toggle to enable notifications.", - "upcoming_empty_location": "You have no location set. Use /setup to select your location.", - "upcoming_empty": "No garbage collection entries found for the next 30 days at **{name}**", - "upcoming": "Upcoming garbage collection:\n\n{entries}" + "cancel": "Verwenden Sie /cancel, wenn Sie diesen Vorgang abbrechen möchten.", + "cancelled": "Die Operation wurde abgebrochen.", + "checkout_deleted": "🗑️ Ihre Daten wurden gelöscht. Wenn Sie diesen Bot wieder benutzen wollen, verwenden Sie bitte den Befehl /setup. Andernfalls löschen/blockieren Sie den Bot und interagieren Sie nicht mehr mit ihm.", + "checkout_deletion": "Prima. Bitte bestätigen Sie, dass Sie Ihre Daten aus dem Bot löschen möchten.\n\nDie folgenden Daten werden gelöscht:\n• Gewählter Sammlungsort\n• Bevorzugte Sprache für alle Nachrichten\n• Zeit der Benachrichtigungen\n• Offset der Benachrichtigungen\n\nVerwenden Sie die Tastatur, um den Vorgang zu bestätigen und fortzusetzen oder /cancel, um ihn abzubrechen.", + "checkout": "Das sind alle Daten, die der Bot hat. Bitte verwenden Sie die Tastatur, um zu wählen, ob Sie Ihre Daten aus dem Bot löschen möchten.", + "commands_removed": "✅ Alle derzeit registrierten Kommandos wurden unregistriert. Die Kommandos werden beim Start des Bots erneut registriert.", + "help": "🔔 Dieser Bot sendet Ihnen Benachrichtigungen über die Müllabfuhr nach Ihrem lokalen Zeitplan.\n\n**Verfügbare Kommandos**\n/help - Diese Menü anzeigen\n/setup - Standort wählen\n/toggle - Deaktivieren/Aktivieren der Benachrichtigungen\n/set_time - Einstellen der Erinnerungszeit\n/set_offset - Offset zwischen Erinnerung und Abholung einstellen\n/upcoming - Zeigt die nächste Abholung an\n/language - Die Sprache des Bots wählen\n/checkout - Daten exportieren oder löschen\n\n💭 Sie können auch vorschlagen, Ihre Stadt/Ihren Bezirk in den Bot aufzunehmen, indem Sie die Administratoren über [diesen Link]({url_contact}) kontaktieren und Ihren Zeitplan angeben.\n\n⚙️ Möchten Sie diesen Bot selbst hosten oder Änderungen vornehmen? Er ist Open-Source, Sie können ihn also forken. Werfen Sie einen Blick auf das [Bot-Repository]({url_repo}) für Details.\n\nViel Spaß beim Benutzen! 🤗", + "import_finished": "Sie haben erfolgreich {count} Einträge eingefügt.", + "import_invalid_date": "Die Einträge enthalten ungültige Datumsformate. Verwenden Sie das Datumsformat **ISO 8601**.", + "import_invalid_filetype": "Ungültige Eingabe. Bitte senden Sie mir eine JSON-Datei mit Einträgen. {cancel_notice}", + "import_invalid": "Dies ist kein gültiges Abfallterminen JSON.", + "import": "Okay. Senden Sie mir ein gültiges JSON.", + "locale_choice": "Prima. Bitte wählen Sie die Sprache mit der Tastatur unten.", + "location_name_empty": "Es konnten keine Orte mit diesem Namen gefunden werden. Versuchen Sie, ihn umzuformulieren, oder stellen Sie sicher, dass Sie dieselbe Sprache und denselben Namen verwenden, wie er von Ihren örtlichen Behörden im Müllabfuhrplan angegeben ist.\n\n{cancel_notice}", + "location_name_invalid": "Bitte senden Sie den Namen des Ortes als Text. {cancel_notice}", + "location_name": "Bitte senden Sie mir einen Standortnamen. Es sollte der Name sein, der im Müllabfuhrplan Ihrer örtlichen Behörde verwendet wird. In der Regel ist dies der Name des Bezirks oder sogar der Stadt selbst.", + "location_select": "Wählen Sie den Ort über die Tastatur aus.", + "reminder": "**Müllabfuhr**\n\nTyp: {Typ}\nDatum: {Datum}\n\nVergessen Sie nicht, Ihre Tonne für die Abholung vorzubereiten!", + "search_nearby_empty": "Es konnten keine Orte in der Nähe gefunden werden. Versuchen wir es mit der Namenssuche.", + "selection_invalid": "Bitte wählen Sie eine gültige Option über die Tastatur aus. {cancel_notice}", + "set_offset_finished": "🔔 Benachrichtigungen Offset wurde aktualisiert! Sie erhalten nun eine Benachrichtigung über die Abholung **{offset} T.** vor der Abholung um {time}. {toggle_notice}", + "set_offset_invalid": "Bitte geben Sie eine gültige ganzzahlige Anzahl von Tagen im Bereich von 0 bis 7 (einschließlich) an. {cancel_notice}", + "set_offset": "Okay. Bitte geben Sie an, wie viele Tage im Voraus Sie eine Benachrichtigung über die Abholung erhalten möchten.", + "set_time_finished": "🔔 Die Benachrichtigungszeit wurde aktualisiert! Sie erhalten nun eine Benachrichtigung über die Abholung {offset} T. vor der Abholung um **{time}**. {toggle_notice}", + "set_time_invalid": "Bitte geben Sie eine gültige Uhrzeit im Format SS:MM an. {cancel_notice}", + "set_time": "Okay. Bitte senden Sie die gewünschte Zeit im Format SS:MM.", + "setup_finished": "✅ Fertig! Ihr Standort ist jetzt **{name}**. Sie werden die Benachrichtigungen über die Müllabfuhr {offset} T. im Voraus um {time} erhalten.", + "setup_retry": "ℹ️ Wenn Sie versuchen möchten, den Speicherort erneut auszuwählen, verwenden Sie den Kommando /setup.", + "setup": "⚙️ Beginnen wir die Konfiguration mit der Suche nach Ihrem Standort.\n\nBitte wählen Sie aus, ob Sie unter den Standorten in Ihrer Nähe suchen möchten oder direkt zur Suche nach dem Standortnamen übergehen wollen.\n\nBeachten Sie, dass der von Ihnen gesendete Standort **NICHT** irgendwo gespeichert wird und nur für die Standortsuche in der Datenbank verwendet wird.", + "start_code_invalid": "🚫 Sie haben den Bot über den Link gestartet, der einen Ort enthält, aber es scheint kein gültiger zu sein. Bitte verwenden Sie den Kommando /setup, um den Standort manuell zu konfigurieren.", + "start_code": "ℹ️ Sie haben den Bot über den Link gestartet, der einen Ort **{name}** enthält.\n\nBitte bestätigen Sie, ob Sie diesen als Ihren Standort verwenden möchten.", + "start_configure": "📍 Lassen Sie uns Ihren Standort konfigurieren. Drücken Sie die Taste auf der Pop-up-Tastatur, um den Vorgang zu starten.", + "start_selection_no": "Gut, Sie sind jetzt auf sich allein gestellt. Bitte verwenden Sie den Kommando /setup, um Ihren Standort zu konfigurieren und Erinnerungen zu erhalten.", + "start_selection_yes": "✅ Fertig! Ihr Standort ist jetzt **{name}**. Sie erhalten Erinnerungen an die Müllabfuhr {offset} T. im Voraus um {time}.\n\nBitte besuchen Sie /help Menü, wenn Sie wissen möchten, wie Sie die Zeit der Benachrichtigungen ändern oder sie deaktivieren können.", + "start": "👋 Herzlich willkommen!\n\nDieser kleine Open-Source-Bot soll Ihnen das Leben etwas erleichtern, indem er Sie über die nächste Müllabfuhr in Ihrer Nähe informiert.\n\nDurch die Nutzung dieses Bots akzeptieren Sie die [Datenschutzbestimmungen]({privacy_policy}), andernfalls blockieren und entfernen Sie diesen Bot bitte vor weiterer Interaktion.\n\nNun ist der offizielle Teil vorbei und Sie können sich mit dem Bot beschäftigen.", + "toggle_disabled": "🔕 Die Benachrichtigungen wurden deaktiviert.", + "toggle_enabled_location": "🔔 Benachrichtigungen wurden aktiviert {offset} T. vor der Sammlung um {time} am **{name}**.", + "toggle_enabled": "🔔 Benachrichtigungen wurden aktiviert {offset} T. vor der Sammlung um {time}. Verwenden Sie /setup, um Ihren Standort auszuwählen.", + "toggle": "Führen Sie /toggle aus, um Benachrichtigungen zu aktivieren.", + "upcoming_empty_location": "Sie haben keinen Standort festgelegt. Verwenden Sie /setup, um Ihren Standort auszuwählen.", + "upcoming_empty": "Keine Müllabfuhr-Einträge für die nächsten 30 Tage bei **{name}** gefunden", + "upcoming": "Bevorstehende Müllabfuhr:\n\n{entries}" }, "force_replies": { - "import": "JSON with garbage entries", - "location_name": "Location name", - "set_offset": "Number of days", - "set_time": "Time as HH:MM" + "import": "JSON mit Abfalltermine", + "location_name": "Ortsname", + "set_offset": "Anzahl der Tage", + "set_time": "Uhrzeit als SS:MM" }, "buttons": { - "delete_confirm": "I agree and want to proceed", - "delete_no": "❌ No, I don't want to delete it", - "delete_yes": "✅ Yes, I want to delete it", - "search_name": "🔎 Search by location name", - "search_nearby": "📍 Search nearby locations", - "start_code_no": "❌ No, I don't want to use it", - "start_code_yes": "✅ Yes, I want to use it", - "start_configure": "⚙️ Let's configure the bot" + "delete_confirm": "Ich stimme zu und möchte fortfahren", + "delete_no": "❌ Nein, ich möchte sie nicht löschen", + "delete_yes": "✅ Ja, ich möchte sie löschen", + "search_name": "🔎 Suche nach Ortsnamen", + "search_nearby": "📍 Suche in der Nähe", + "start_code_no": "❌ Nein, ich will es nicht benutzen", + "start_code_yes": "✅ Ja, ich möchte es benutzen", + "start_configure": "⚙️ Konfigurieren wir den Bot" }, "callbacks": { - "locale_set": "Your language now is: {locale}" + "locale_set": "Ihre Sprache ist jetzt: {locale}" } } From 7fe6f24538f205a2fc993aa895e6b02f8b9d59ff Mon Sep 17 00:00:00 2001 From: Weblate Date: Thu, 31 Aug 2023 12:08:01 +0300 Subject: [PATCH 23/36] Update translation files Updated by "Cleanup translation files" hook in Weblate. Co-authored-by: Weblate Translate-URL: https://weblate.end-play.xyz/projects/garbagereminder/telegrambot/ Translation: GarbageReminder/TelegramBot --- locale/uk.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/locale/uk.json b/locale/uk.json index 263a3c0..528d924 100644 --- a/locale/uk.json +++ b/locale/uk.json @@ -77,7 +77,6 @@ "upcoming": "Найближчі вивози сміття:\n\n{entries}" }, "buttons": { - "configure": "Давайте налаштуємо бота", "delete_confirm": "Я погоджуюсь і хочу продовжити", "delete_no": "❌ Ні, я не хочу видаляти їх", "delete_yes": "✅ Так, я хочу видалити їх", @@ -96,4 +95,4 @@ "set_offset": "Кількість днів", "set_time": "Час у вигляді ГГ:ХХ" } -} \ No newline at end of file +} From 13ca09790968b0eac6f4068afb0e8179b788292d Mon Sep 17 00:00:00 2001 From: profitroll Date: Wed, 6 Sep 2023 22:26:44 +0200 Subject: [PATCH 24/36] Fixed missing locale --- plugins/commands/upcoming.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/commands/upcoming.py b/plugins/commands/upcoming.py index d468b0d..163fd09 100644 --- a/plugins/commands/upcoming.py +++ b/plugins/commands/upcoming.py @@ -46,7 +46,7 @@ async def command_upcoming(app: PyroClient, message: Message): entries_text = "\n\n".join( [ - f"**{entry.date.strftime(app._('date', 'formats', locale=user.locale))}**:\n{app._(str(entry.garbage_type.value), 'garbage_types')}" + f"**{entry.date.strftime(app._('date', 'formats', locale=user.locale))}**:\n{app._(str(entry.garbage_type.value), 'garbage_types', locale=user.locale)}" for entry in entries ] ) From b0f76f4c49c62fecf73af1000a82bfa3d0f73a4d Mon Sep 17 00:00:00 2001 From: profitroll Date: Mon, 11 Sep 2023 21:16:31 +0200 Subject: [PATCH 25/36] from_dict() implemented --- classes/pyrouser.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/classes/pyrouser.py b/classes/pyrouser.py index ef7d286..abb4a50 100644 --- a/classes/pyrouser.py +++ b/classes/pyrouser.py @@ -71,6 +71,15 @@ class PyroUser: return cls(**db_entry) + @classmethod + async def from_dict(cls, **kwargs): + if "location" in kwargs: + try: + kwargs["location"] = await Location.get(kwargs["location"]) # type: ignore + except ValueError: + kwargs["location"] = None # type: ignore + return cls(**kwargs) + async def update_locale(self, locale: Union[str, None]) -> None: """Change user's locale stored in the database. From 3fa2f5a8009997f3dd6b4c2c271125d2e6803792 Mon Sep 17 00:00:00 2001 From: profitroll Date: Sun, 24 Sep 2023 23:47:09 +0200 Subject: [PATCH 26/36] Attempt to work around timezones --- classes/location.py | 8 ++++++- locale/de.json | 4 ++-- locale/en.json | 2 +- locale/ru.json | 4 ++-- locale/uk.json | 2 +- modules/reminder.py | 18 +++++++--------- modules/utils.py | 38 ++++++++++++++++++++++++++++++++++ plugins/commands/set_offset.py | 12 +++++++++-- plugins/commands/set_time.py | 14 +++++++++++-- plugins/commands/setup.py | 8 ++++--- plugins/commands/start.py | 8 ++++--- plugins/commands/toggle.py | 6 +++++- plugins/commands/upcoming.py | 11 +++------- 13 files changed, 99 insertions(+), 36 deletions(-) diff --git a/classes/location.py b/classes/location.py index 26d9ebd..5f51a90 100644 --- a/classes/location.py +++ b/classes/location.py @@ -1,6 +1,9 @@ from dataclasses import dataclass +from typing import Union from bson import ObjectId +from pytz import timezone as pytz_timezone +from pytz.tzinfo import BaseTzInfo, DstTzInfo from classes.point import Point from modules.database import col_locations @@ -22,7 +25,7 @@ class Location: name: str location: Point country: int - timezone: str + timezone: Union[BaseTzInfo, DstTzInfo] @classmethod async def get(cls, id: int): @@ -32,6 +35,7 @@ class Location: raise ValueError(f"No location with ID {id} found.") db_entry["location"] = Point(*db_entry["location"]) # type: ignore + db_entry["timezone"] = pytz_timezone(db_entry["timezone"]) # type: ignore return cls(**db_entry) @@ -43,6 +47,7 @@ class Location: raise ValueError(f"No location with name {name} found.") db_entry["location"] = Point(*db_entry["location"]) # type: ignore + db_entry["timezone"] = pytz_timezone(db_entry["timezone"]) # type: ignore return cls(**db_entry) @@ -54,5 +59,6 @@ class Location: raise ValueError(f"No location near {lat}, {lon} found.") db_entry["location"] = Point(*db_entry["location"]) # type: ignore + db_entry["timezone"] = pytz_timezone(db_entry["timezone"]) # type: ignore return cls(**db_entry) diff --git a/locale/de.json b/locale/de.json index 708b858..9c84eda 100644 --- a/locale/de.json +++ b/locale/de.json @@ -46,6 +46,7 @@ "import_invalid": "Dies ist kein gültiges Abfallterminen JSON.", "import": "Okay. Senden Sie mir ein gültiges JSON.", "locale_choice": "Prima. Bitte wählen Sie die Sprache mit der Tastatur unten.", + "location_empty": "Sie haben keinen Standort festgelegt. Verwenden Sie /setup, um Ihren Standort auszuwählen.", "location_name_empty": "Es konnten keine Orte mit diesem Namen gefunden werden. Versuchen Sie, ihn umzuformulieren, oder stellen Sie sicher, dass Sie dieselbe Sprache und denselben Namen verwenden, wie er von Ihren örtlichen Behörden im Müllabfuhrplan angegeben ist.\n\n{cancel_notice}", "location_name_invalid": "Bitte senden Sie den Namen des Ortes als Text. {cancel_notice}", "location_name": "Bitte senden Sie mir einen Standortnamen. Es sollte der Name sein, der im Müllabfuhrplan Ihrer örtlichen Behörde verwendet wird. In der Regel ist dies der Name des Bezirks oder sogar der Stadt selbst.", @@ -72,7 +73,6 @@ "toggle_enabled_location": "🔔 Benachrichtigungen wurden aktiviert {offset} T. vor der Sammlung um {time} am **{name}**.", "toggle_enabled": "🔔 Benachrichtigungen wurden aktiviert {offset} T. vor der Sammlung um {time}. Verwenden Sie /setup, um Ihren Standort auszuwählen.", "toggle": "Führen Sie /toggle aus, um Benachrichtigungen zu aktivieren.", - "upcoming_empty_location": "Sie haben keinen Standort festgelegt. Verwenden Sie /setup, um Ihren Standort auszuwählen.", "upcoming_empty": "Keine Müllabfuhr-Einträge für die nächsten 30 Tage bei **{name}** gefunden", "upcoming": "Bevorstehende Müllabfuhr:\n\n{entries}" }, @@ -95,4 +95,4 @@ "callbacks": { "locale_set": "Ihre Sprache ist jetzt: {locale}" } -} +} \ No newline at end of file diff --git a/locale/en.json b/locale/en.json index 9f96544..1509a69 100644 --- a/locale/en.json +++ b/locale/en.json @@ -46,6 +46,7 @@ "import_invalid": "This is not a valid garbage collection JSON.", "import": "Alright. Send me a valid JSON.", "locale_choice": "Alright. Please choose the language using keyboard below.", + "location_empty": "You have no location set. Use /setup to select your location first.", "location_name_empty": "Could not find any locations by this name. Try rephrasing it or make sure you use the same location language and name itself as it in written by your local authorities in garbage collection schedule.\n\n{cancel_notice}", "location_name_invalid": "Please, send the name of the location as a text. {cancel_notice}", "location_name": "Please, send me a location name. It should be the name used in your local authorities' garbage collection schedule. This usually is a name of the district or even the town itself.", @@ -72,7 +73,6 @@ "toggle_enabled_location": "🔔 Notifications have been enabled {offset} d. before garbage collection at {time} at the **{name}**.", "toggle_enabled": "🔔 Notifications have been enabled {offset} d. before garbage collection at {time}. Use /setup to select your location.", "toggle": "Execute /toggle to enable notifications.", - "upcoming_empty_location": "You have no location set. Use /setup to select your location.", "upcoming_empty": "No garbage collection entries found for the next 30 days at **{name}**", "upcoming": "Upcoming garbage collection:\n\n{entries}" }, diff --git a/locale/ru.json b/locale/ru.json index 5f1168f..27adcea 100644 --- a/locale/ru.json +++ b/locale/ru.json @@ -22,6 +22,7 @@ "import_invalid": "Це недійсний JSON даних збору сміття.", "import": "Гаразд. Надішліть правильний JSON.", "locale_choice": "Гаразд. Будь ласка, оберіть мову за допомогою клавіатури нижче.", + "location_empty": "У Вас не встановлено локацію вивозу. Оберіть свою локацію за допомогою /setup.", "location_name_empty": "Не вдалося знайти жодного населеного пункту з такою назвою. Спробуйте перефразувати назву або переконайтеся, що Ви використовуєте ту саму мову та назву, що й місцева влада у графіку вивезення сміття.\n\n{cancel_notice}", "location_name_invalid": "Будь ласка, надішліть назву місця у вигляді тексту. {cancel_notice}", "location_name": "Будь ласка, надішліть мені назву населеного пункту. Це має бути назва, яка використовується у графіку вивезення сміття Вашою місцевою владою. Зазвичай це назва району або міста.", @@ -47,7 +48,6 @@ "toggle_enabled_location": "🔔 Сповіщення увімкнено за {offset} д. до вивезення сміття о {time} для локації **{name}**.", "toggle_enabled": "🔔 Сповіщення було увімкнено за {offset} д. до вивезення сміття о {time}. Оберіть своє розташування за допомогою /setup.", "toggle": "Використовуйте /toggle, щоб увімкнути сповіщення.", - "upcoming_empty_location": "У Вас не встановлено локацію вивозу. Оберіть свою локацію за допомогою /setup.", "upcoming_empty": "Не знайдено записів про вивезення сміття на найближчі 30 днів для **{name}**", "upcoming": "Найближчі вивози сміття:\n\n{entries}" }, @@ -95,4 +95,4 @@ "set_offset": "Кількість днів", "set_time": "Час у вигляді ГГ:ХХ" } -} +} \ No newline at end of file diff --git a/locale/uk.json b/locale/uk.json index 263a3c0..4ed1758 100644 --- a/locale/uk.json +++ b/locale/uk.json @@ -46,6 +46,7 @@ "import_invalid": "Це недійсний JSON даних збору сміття.", "import": "Гаразд. Надішліть правильний JSON.", "locale_choice": "Гаразд. Будь ласка, оберіть мову за допомогою клавіатури нижче.", + "location_empty": "У Вас не встановлено локацію вивозу. Оберіть свою локацію за допомогою /setup.", "location_name_empty": "Не вдалося знайти жодного населеного пункту з такою назвою. Спробуйте перефразувати назву або переконайтеся, що Ви використовуєте ту саму мову та назву, що й місцева влада у графіку вивезення сміття.\n\n{cancel_notice}", "location_name_invalid": "Будь ласка, надішліть назву місця у вигляді тексту. {cancel_notice}", "location_name": "Будь ласка, надішліть мені назву населеного пункту. Це має бути назва, яка використовується у графіку вивезення сміття Вашою місцевою владою. Зазвичай це назва району або міста.", @@ -72,7 +73,6 @@ "toggle_enabled_location": "🔔 Сповіщення увімкнено за {offset} д. до вивезення сміття о {time} для локації **{name}**.", "toggle_enabled": "🔔 Сповіщення було увімкнено за {offset} д. до вивезення сміття о {time}. Оберіть своє розташування за допомогою /setup.", "toggle": "Використовуйте /toggle, щоб увімкнути сповіщення.", - "upcoming_empty_location": "У Вас не встановлено локацію вивозу. Оберіть свою локацію за допомогою /setup.", "upcoming_empty": "Не знайдено записів про вивезення сміття на найближчі 30 днів для **{name}**", "upcoming": "Найближчі вивози сміття:\n\n{entries}" }, diff --git a/modules/reminder.py b/modules/reminder.py index cf2b939..a662e36 100644 --- a/modules/reminder.py +++ b/modules/reminder.py @@ -2,25 +2,25 @@ import logging from datetime import datetime, timedelta, timezone from libbot.pyrogram.classes import PyroClient -from pytz import timezone as pytz_timezone from classes.enums import GarbageType from classes.location import Location from classes.pyrouser import PyroUser from modules.database import col_entries, col_users +from modules.utils import from_utc logger = logging.getLogger(__name__) async def remind(app: PyroClient) -> None: - now = datetime.now() + utcnow = datetime.utcnow() users = await col_users.find( - {"time_hour": now.hour, "time_minute": now.minute} + {"time_hour": utcnow.hour, "time_minute": utcnow.minute} ).to_list() for user_db in users: - user = PyroUser(**user_db) + user = await PyroUser.from_dict(**user_db) if not user.enabled or user.location is None: continue @@ -30,12 +30,10 @@ async def remind(app: PyroClient) -> None: except ValueError: continue - user_date = ( - datetime.now(pytz_timezone(location.timezone)).replace( - second=0, microsecond=0 - ) - + timedelta(days=user.offset) - ).replace(tzinfo=timezone.utc) + user_date = from_utc( + datetime(1970, 1, 1, user.time_hour, user.time_minute), + user.location.timezone.zone, + ) entries = await col_entries.find( { diff --git a/modules/utils.py b/modules/utils.py index e69de29..aaece4b 100644 --- a/modules/utils.py +++ b/modules/utils.py @@ -0,0 +1,38 @@ +from datetime import datetime +from typing import Union + +from pytz import UTC +from pytz import timezone as pytz_timezone + + +def to_utc(date: datetime, timezone: Union[str, None] = None) -> datetime: + """Move timezone unaware datetime object to UTC timezone and return it. + + ### Args: + * date (`datetime`): Datetime to be converted. + * timezone (`Union[str, None] = None`): Timezone name (must be pytz-compatible). Defaults to `None` (UTC). + + ### Returns: + * `datetime`: Timezone unaware datetime in UTC with timezone's offset applied to it. + """ + timezone = "UTC" if timezone is None else timezone + return pytz_timezone(timezone).localize(date).astimezone(UTC).replace(tzinfo=None) + + +def from_utc(date: datetime, timezone: Union[str, None] = None) -> datetime: + """Move timezone unaware datetime object to the timezone specified and return it. + + ### Args: + * date (`datetime`): Datetime to be converted. + * timezone (`Union[str, None] = None`): Timezone name (must be pytz-compatible). Defaults to `None` (UTC). + + ### Returns: + * `datetime`: Timezone unaware datetime in timezone provided with offset from UTC applied to it. + """ + timezone = "UTC" if timezone is None else timezone + return ( + pytz_timezone("UTC") + .localize(date) + .astimezone(pytz_timezone(timezone)) + .replace(tzinfo=None) + ) diff --git a/plugins/commands/set_offset.py b/plugins/commands/set_offset.py index c0981de..30fce12 100644 --- a/plugins/commands/set_offset.py +++ b/plugins/commands/set_offset.py @@ -7,6 +7,7 @@ from pyrogram.types import ForceReply, Message, ReplyKeyboardRemove from classes.pyroclient import PyroClient from modules import custom_filters +from modules.utils import from_utc logger = logging.getLogger(__name__) @@ -17,6 +18,12 @@ logger = logging.getLogger(__name__) async def command_set_offset(app: PyroClient, message: Message): user = await app.find_user(message.from_user) + if user.location is None: + await message.reply_text( + app._("location_empty", "messages", locale=user.locale) + ) + return + await message.reply_text( app._("set_offset", "messages", locale=user.locale), reply_markup=ForceReply( @@ -58,8 +65,9 @@ async def command_set_offset(app: PyroClient, message: Message): logger.info("User %s has set offset to %s", user.id, offset) - garbage_time = datetime( - 1970, 1, 1, hour=user.time_hour, minute=user.time_minute + garbage_time = from_utc( + datetime(1970, 1, 1, user.time_hour, user.time_minute), + None if user.location is None else user.location.timezone.zone, ).strftime(app._("time", "formats")) await answer.reply_text( diff --git a/plugins/commands/set_time.py b/plugins/commands/set_time.py index fbee4fb..5e4fb40 100644 --- a/plugins/commands/set_time.py +++ b/plugins/commands/set_time.py @@ -7,6 +7,7 @@ from pyrogram.types import ForceReply, Message, ReplyKeyboardRemove from classes.pyroclient import PyroClient from modules import custom_filters +from modules.utils import to_utc logger = logging.getLogger(__name__) @@ -17,6 +18,12 @@ logger = logging.getLogger(__name__) async def command_set_time(app: PyroClient, message: Message): user = await app.find_user(message.from_user) + if user.location is None: + await message.reply_text( + app._("location_empty", "messages", locale=user.locale) + ) + return + await message.reply_text( app._("set_time", "messages", locale=user.locale), reply_markup=ForceReply( @@ -48,7 +55,10 @@ async def command_set_time(app: PyroClient, message: Message): break - user_time = datetime.strptime(answer.text, "%H:%M") + parsed_time = datetime.strptime(answer.text, "%H:%M").replace( + year=1970, month=1, day=1, second=0, microsecond=0 + ) + user_time = to_utc(parsed_time, user.location.timezone.zone) await user.update_time(hour=user_time.hour, minute=user_time.minute) @@ -58,7 +68,7 @@ async def command_set_time(app: PyroClient, message: Message): user_time.strftime("%H:%M"), ) - garbage_time = user_time.strftime(app._("time", "formats")) + garbage_time = parsed_time.strftime(app._("time", "formats")) await answer.reply_text( app._("set_time_finished", "messages", locale=user.locale).format( diff --git a/plugins/commands/setup.py b/plugins/commands/setup.py index 5bebffe..3790831 100644 --- a/plugins/commands/setup.py +++ b/plugins/commands/setup.py @@ -11,6 +11,7 @@ from classes.pyroclient import PyroClient from modules import custom_filters from modules.search_name import search_name from modules.search_nearby import search_nearby +from modules.utils import from_utc logger = logging.getLogger(__name__) @@ -73,9 +74,10 @@ async def command_setup(app: PyroClient, message: Message): await user.update_location(location.id) - user_time = datetime(1970, 1, 1, user.time_hour, user.time_minute).strftime( - app._("time", "formats", locale=user.locale) - ) + user_time = from_utc( + datetime(1970, 1, 1, user.time_hour, user.time_minute), + None if user.location is None else user.location.timezone.zone, + ).strftime(app._("time", "formats", locale=user.locale)) await message.reply_text( app._("setup_finished", "messages", locale=user.locale).format( diff --git a/plugins/commands/start.py b/plugins/commands/start.py index 3df7e34..6cdd43e 100644 --- a/plugins/commands/start.py +++ b/plugins/commands/start.py @@ -11,6 +11,7 @@ from pyrogram.types import ( from classes.pyroclient import PyroClient from modules import custom_filters +from modules.utils import from_utc @PyroClient.on_message( @@ -85,9 +86,10 @@ async def command_start(app: PyroClient, message: Message): await user.update_location(location.id) - user_time = datetime(1970, 1, 1, user.time_hour, user.time_minute).strftime( - app._("time", "formats", locale=user.locale) - ) + user_time = from_utc( + datetime(1970, 1, 1, user.time_hour, user.time_minute), + None if user.location is None else user.location.timezone.zone, + ).strftime(app._("time", "formats", locale=user.locale)) await answer.reply_text( app._("start_selection_yes", "messages", locale=user.locale).format( name=location.name, offset=user.offset, time=user_time diff --git a/plugins/commands/toggle.py b/plugins/commands/toggle.py index ac97f2b..1b67107 100644 --- a/plugins/commands/toggle.py +++ b/plugins/commands/toggle.py @@ -5,6 +5,7 @@ from pyrogram.types import Message from classes.pyroclient import PyroClient from modules import custom_filters +from modules.utils import from_utc @PyroClient.on_message( @@ -21,7 +22,10 @@ async def command_toggle(app: PyroClient, message: Message): ) return - user_time = datetime(1970, 1, 1, user.time_hour, user.time_minute).strftime("%H:%M") + user_time = from_utc( + datetime(1970, 1, 1, user.time_hour, user.time_minute), + None if user.location is None else user.location.timezone.zone, + ).strftime(app._("time", "formats")) if user.location is None: await message.reply_text( diff --git a/plugins/commands/upcoming.py b/plugins/commands/upcoming.py index 163fd09..9c67396 100644 --- a/plugins/commands/upcoming.py +++ b/plugins/commands/upcoming.py @@ -2,7 +2,6 @@ from datetime import datetime, timedelta, timezone from pyrogram import filters from pyrogram.types import Message -from pytz import timezone as pytz_timezone from classes.garbage_entry import GarbageEntry from classes.pyroclient import PyroClient @@ -18,19 +17,15 @@ async def command_upcoming(app: PyroClient, message: Message): if user.location is None: await message.reply_text( - app._("upcoming_empty_location", "messages", locale=user.locale) + app._("location_empty", "messages", locale=user.locale) ) return date_min = ( - datetime.now(pytz_timezone(user.location.timezone)).replace( - second=0, microsecond=0 - ) + datetime.now(user.location.timezone).replace(second=0, microsecond=0) ).replace(tzinfo=timezone.utc) date_max = ( - datetime.now(pytz_timezone(user.location.timezone)).replace( - second=0, microsecond=0 - ) + datetime.now(user.location.timezone).replace(second=0, microsecond=0) + timedelta(days=30) ).replace(tzinfo=timezone.utc) From ac7e26b25fddd51d0d94644d53f590f31d5ee883 Mon Sep 17 00:00:00 2001 From: Renovate Date: Sat, 14 Oct 2023 00:28:56 +0300 Subject: [PATCH 27/36] Update dependency uvloop to v0.18.0 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 39040b9..f667897 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,7 @@ mongodb-migrations==1.3.0 pykeyboard==0.1.5 tgcrypto==1.2.5 ujson>=5.0.0 -uvloop==0.17.0 +uvloop==0.18.0 --extra-index-url https://git.end-play.xyz/api/packages/profitroll/pypi/simple async_pymongo==0.1.4 libbot[speed,pyrogram]==2.0.1 \ No newline at end of file From 192602471b06e1bd2d65e673b088612f6bc634e8 Mon Sep 17 00:00:00 2001 From: profitroll Date: Sun, 15 Oct 2023 22:20:12 +0200 Subject: [PATCH 28/36] Added debug key --- config_example.json | 1 + main.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/config_example.json b/config_example.json index 40c264a..cff73b1 100644 --- a/config_example.json +++ b/config_example.json @@ -1,5 +1,6 @@ { "locale": "en", + "debug": false, "bot": { "owner": 0, "api_id": 0, diff --git a/main.py b/main.py index af04103..6aa78c8 100644 --- a/main.py +++ b/main.py @@ -12,7 +12,7 @@ from modules.migrator import migrate_database from modules.scheduler import scheduler logging.basicConfig( - level=logging.INFO, + level=logging.DEBUG if sync.config_get("debug") else logging.INFO, format="%(name)s.%(funcName)s | %(levelname)s | %(message)s", datefmt="[%X]", ) From 842218859cd5118c0fadc41d8d5cd576b6186958 Mon Sep 17 00:00:00 2001 From: profitroll Date: Sun, 15 Oct 2023 22:21:10 +0200 Subject: [PATCH 29/36] Yet another timezones fix --- modules/reminder.py | 25 +++++++++++++++++++++---- plugins/commands/set_time.py | 5 ++++- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/modules/reminder.py b/modules/reminder.py index a662e36..e4b5794 100644 --- a/modules/reminder.py +++ b/modules/reminder.py @@ -1,6 +1,7 @@ import logging -from datetime import datetime, timedelta, timezone +from datetime import datetime, timedelta +from bson import json_util from libbot.pyrogram.classes import PyroClient from classes.enums import GarbageType @@ -15,11 +16,21 @@ logger = logging.getLogger(__name__) async def remind(app: PyroClient) -> None: utcnow = datetime.utcnow() + logger.debug("Performing reminder lookup for %s (UTCNOW)", utcnow) + users = await col_users.find( {"time_hour": utcnow.hour, "time_minute": utcnow.minute} ).to_list() + logger.debug( + "Found following reminders for %s (UTC NOW): %s", + utcnow, + json_util.dumps(users, indent=None), + ) + for user_db in users: + logger.debug("Processing user %s...", json_util.dumps(user_db, indent=None)) + user = await PyroUser.from_dict(**user_db) if not user.enabled or user.location is None: @@ -31,20 +42,26 @@ async def remind(app: PyroClient) -> None: continue user_date = from_utc( - datetime(1970, 1, 1, user.time_hour, user.time_minute), + datetime.utcnow() + timedelta(days=user.offset), user.location.timezone.zone, - ) + ).replace(hour=0, minute=0, second=0, microsecond=0) entries = await col_entries.find( { "locations": location.id, - "date": user_date.replace(hour=0, minute=0), + "date": user_date, } ).to_list() logger.info("Entries of %s for %s: %s", user.id, user_date, entries) for entry in entries: + logger.debug( + "Sending %s notification about %s", + user.id, + json_util.dumps(entry, indent=None), + ) + try: garbage_type = app._( str(GarbageType(entry["garbage_type"]).value), diff --git a/plugins/commands/set_time.py b/plugins/commands/set_time.py index 5e4fb40..166b384 100644 --- a/plugins/commands/set_time.py +++ b/plugins/commands/set_time.py @@ -55,9 +55,12 @@ async def command_set_time(app: PyroClient, message: Message): break + now = datetime.now() + parsed_time = datetime.strptime(answer.text, "%H:%M").replace( - year=1970, month=1, day=1, second=0, microsecond=0 + year=now.year, month=now.month, day=now.day, second=0, microsecond=0 ) + user_time = to_utc(parsed_time, user.location.timezone.zone) await user.update_time(hour=user_time.hour, minute=user_time.minute) From b5143ed961bab60d4d96eb5947ea76f014e97c74 Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 23 Oct 2023 02:09:08 +0300 Subject: [PATCH 30/36] Update dependency uvloop to v0.19.0 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index f667897..9c10296 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,7 @@ mongodb-migrations==1.3.0 pykeyboard==0.1.5 tgcrypto==1.2.5 ujson>=5.0.0 -uvloop==0.18.0 +uvloop==0.19.0 --extra-index-url https://git.end-play.xyz/api/packages/profitroll/pypi/simple async_pymongo==0.1.4 libbot[speed,pyrogram]==2.0.1 \ No newline at end of file From c9c2d298ef2cec11e0b76238a4ddd30389ec4d95 Mon Sep 17 00:00:00 2001 From: profitroll Date: Sun, 29 Oct 2023 18:18:43 +0100 Subject: [PATCH 31/36] Work being done on #11 --- classes/importer/abstract.py | 20 +++++++++ classes/importer/csv.py | 64 +++++++++++++++++++++++++++ classes/importer/json.py | 56 ++++++++++++++++++++++++ locale/en.json | 6 +-- plugins/commands/import.py | 85 ++++++++++++++---------------------- 5 files changed, 176 insertions(+), 55 deletions(-) create mode 100644 classes/importer/abstract.py create mode 100644 classes/importer/csv.py create mode 100644 classes/importer/json.py diff --git a/classes/importer/abstract.py b/classes/importer/abstract.py new file mode 100644 index 0000000..096ec11 --- /dev/null +++ b/classes/importer/abstract.py @@ -0,0 +1,20 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod +from typing import Any + + +class Importer(ABC): + """ + The Importer class represents the object with + functionality to import/export garbage collection + records and convert them to other object types. + """ + + @abstractmethod + async def import_data(self, data: Any) -> None: + pass + + @abstractmethod + async def export_data(self, data: Any) -> None: + pass diff --git a/classes/importer/csv.py b/classes/importer/csv.py new file mode 100644 index 0000000..c876f92 --- /dev/null +++ b/classes/importer/csv.py @@ -0,0 +1,64 @@ +from codecs import decode +from csv import DictReader +from datetime import datetime +from typing import Any, Dict, List, Union + +from bson import ObjectId + +from classes.importer.abstract import Importer +from modules.database import col_entries + + +class ImporterCSV(Importer): + """ + The ImporterCSV class represents the object with + functionality to import/export garbage collection + records and convert them to other object types + from CSV files. + """ + + def __init__(self): + super(Importer, self).__init__() + + async def import_data(self, data: bytes) -> List[ObjectId]: + entries: List[Dict[str, Any]] = list( + DictReader(decode(data).split("\n"), delimiter=";") + ) + + for entry in entries: + entry["locations"] = ( + [int(entry["locations"])] + if "," not in entry["locations"] + else [int(id) for id in entry["locations"].split(",")] + ) + entry["garbage_type"] = int(entry["garbage_type"]) + + for key in ("locations", "garbage_type", "date"): + if ( + key not in entry + or (key == "garbage_type" and not isinstance(entry[key], int)) + or (key == "locations" and not isinstance(entry[key], list)) + ): + raise ValueError + + if key == "date": + try: + datetime.fromisoformat(str(entry[key])) + except (ValueError, TypeError) as exc: + raise ValueError from exc + + entries_clean: List[Dict[str, Union[str, int, datetime]]] = [ + { + "locations": entry["locations"], + "garbage_type": entry["garbage_type"], + "date": datetime.fromisoformat(str(entry["date"])), + } + for entry in entries + ] + + inserted = await col_entries.insert_many(entries_clean) + + return [] if inserted is None else inserted.inserted_ids + + async def export_data(self, data: Any) -> Any: + return None diff --git a/classes/importer/json.py b/classes/importer/json.py new file mode 100644 index 0000000..86afaad --- /dev/null +++ b/classes/importer/json.py @@ -0,0 +1,56 @@ +from datetime import datetime +from typing import Any, Dict, List, Union + +from bson import ObjectId +from ujson import loads + +from classes.importer.abstract import Importer +from modules.database import col_entries + + +class ImporterJSON(Importer): + """ + The ImporterJSON class represents the object with + functionality to import/export garbage collection + records and convert them to other object types + from JSON files. + """ + + def __init__(self): + super(Importer, self).__init__() + + async def import_data(self, data: bytes) -> List[ObjectId]: + entries: List[Dict[str, Any]] = loads(data) + + for entry in entries: + for key in ("locations", "garbage_type", "date"): + if ( + key not in entry + or (key == "garbage_type" and not isinstance(entry[key], int)) + or (key == "locations" and not isinstance(entry[key], list)) + ): + print("keys", entry) + raise ValueError + + if key == "date": + try: + datetime.fromisoformat(str(entry[key])) + except (ValueError, TypeError) as exc: + print("date", entry) + raise ValueError from exc + + entries_clean: List[Dict[str, Union[str, int, datetime]]] = [ + { + "locations": entry["locations"], + "garbage_type": entry["garbage_type"], + "date": datetime.fromisoformat(str(entry["date"])), + } + for entry in entries + ] + + inserted = await col_entries.insert_many(entries_clean) + + return [] if inserted is None else inserted.inserted_ids + + async def export_data(self, data: Any) -> Any: + return None diff --git a/locale/en.json b/locale/en.json index 1509a69..b555e9e 100644 --- a/locale/en.json +++ b/locale/en.json @@ -42,9 +42,9 @@ "help": "🔔 This bot sends you notifications about garbage collection according to your local schedule.\n\n**Available commands**\n/help - Show this message\n/setup - Select the location\n/toggle - Disable/enable the reminders\n/set_time - Set the reminders' time\n/set_offset - Set offset between reminders and collection\n/upcoming - Show the upcoming collection\n/language - Select the bot's language\n/checkout - Export or remove your data\n\n💭 You can also suggest adding your town/district to the bot by contacting the admins using [this link]({url_contact}) and providing your schedule.\n\n⚙️ Want to host this bot yourself or make some changes? It's open-source, so you can basically fork it. Take a look at [bot's repository]({url_repo}) for details.\n\nHappy using! 🤗", "import_finished": "You have successfully inserted {count} entries.", "import_invalid_date": "Entries contain invalid date formats. Use **ISO 8601** date format.", - "import_invalid_filetype": "Invalid input. Please, send me a JSON file with entries. {cancel_notice}", - "import_invalid": "This is not a valid garbage collection JSON.", - "import": "Alright. Send me a valid JSON.", + "import_invalid_filetype": "Invalid input. Please, send me a JSON or CSV file with entries. {cancel_notice}", + "import_invalid": "This is not a valid garbage collection file.", + "import": "Alright. Send me a valid file. It can be in JSON or CSV format. Read more about supported formats in the documentation", "locale_choice": "Alright. Please choose the language using keyboard below.", "location_empty": "You have no location set. Use /setup to select your location first.", "location_name_empty": "Could not find any locations by this name. Try rephrasing it or make sure you use the same location language and name itself as it in written by your local authorities in garbage collection schedule.\n\n{cancel_notice}", diff --git a/plugins/commands/import.py b/plugins/commands/import.py index 64c38e1..9a68c56 100644 --- a/plugins/commands/import.py +++ b/plugins/commands/import.py @@ -1,33 +1,26 @@ -from datetime import datetime -from typing import List, Mapping, Union - from convopyro import listen_message from pyrogram import filters from pyrogram.types import ForceReply, Message, ReplyKeyboardRemove -from ujson import loads +from classes.importer.csv import ImporterCSV +from classes.importer.json import ImporterJSON from classes.pyroclient import PyroClient from modules import custom_filters -from modules.database import col_entries @PyroClient.on_message( - ~filters.scheduled & filters.private & custom_filters.owner & filters.command(["import"], prefixes=["/"]) & ~custom_filters.context # type: ignore + ~filters.scheduled & filters.private & custom_filters.owner & filters.command(["import"], prefixes=["/"]) # type: ignore ) async def command_import(app: PyroClient, message: Message): user = await app.find_user(message.from_user) await message.reply_text( app._("import", "messages", locale=user.locale), - reply_markup=ForceReply( - placeholder=app._("import", "force_replies", locale=user.locale) - ), + reply_markup=ForceReply(placeholder=""), ) while True: - app.contexts.append(message.from_user.id) answer = await listen_message(app, message.chat.id, 300) - app.contexts.remove(message.from_user.id) if answer is None or answer.text == "/cancel": await message.reply_text( @@ -36,7 +29,10 @@ async def command_import(app: PyroClient, message: Message): ) return - if answer.document is None or answer.document.mime_type != "application/json": + if answer.document is None or answer.document.mime_type not in [ + "application/json", + "text/csv", + ]: await answer.reply_text( app._("import_invalid_filetype", "messages", locale=user.locale).format( cancel_notice=app._("cancel", "messages", locale=user.locale) @@ -48,51 +44,36 @@ async def command_import(app: PyroClient, message: Message): file = await app.download_media(answer, in_memory=True) - entries: List[Mapping[str, Union[str, int]]] = loads(bytes(file.getbuffer())) # type: ignore + data: bytes = bytes(file.getbuffer()) # type: ignore - for entry in entries: - if not isinstance(entries, list): - await answer.reply_text( - app._("import_invalid", "messages", locale=user.locale), - reply_markup=ReplyKeyboardRemove(), - ) - return + # I'd like to replace it with switch-case, but 3.9 compatibility + # is still more important to be there. Although refactor may be + # done in the near future as Python 3.9 EOL gets nearer. + if answer.document.mime_type == "application/json": + importer = ImporterJSON() + elif answer.document.mime_type == "text/csv": + importer = ImporterCSV() + else: + await answer.reply_text( + app._("import_invalid_filetype", "messages", locale=user.locale).format( + cancel_notice="" + ), + reply_markup=ReplyKeyboardRemove(), + ) + return - for key in ("locations", "garbage_type", "date"): - if ( - key not in entry - or (key == "garbage_type" and not isinstance(entry[key], int)) - or (key == "locations" and not isinstance(entry[key], list)) - ): - await answer.reply_text( - app._("import_invalid", "messages", locale=user.locale), - reply_markup=ReplyKeyboardRemove(), - ) - return - if key == "date": - try: - datetime.fromisoformat(str(entry[key])) - except (ValueError, TypeError): - await answer.reply_text( - app._("import_invalid_date", "messages", locale=user.locale), - reply_markup=ReplyKeyboardRemove(), - ) - return - - entries_clean: List[Mapping[str, Union[str, int, datetime]]] = [ - { - "locations": entry["locations"], - "garbage_type": entry["garbage_type"], - "date": datetime.fromisoformat(str(entry["date"])), - } - for entry in entries - ] - - await col_entries.insert_many(entries_clean) + try: + import_result = await importer.import_data(data) + except ValueError: + await answer.reply_text( + app._("import_invalid", "messages", locale=user.locale), + reply_markup=ReplyKeyboardRemove(), + ) + return await answer.reply_text( app._("import_finished", "messages", locale=user.locale).format( - count=len(entries_clean) + count=len(import_result) ), reply_markup=ReplyKeyboardRemove(), ) From 75fc6c9b6d5a1c71ddebf2d7e64e114cae599e80 Mon Sep 17 00:00:00 2001 From: Weblate Date: Sun, 29 Oct 2023 19:55:39 +0200 Subject: [PATCH 32/36] Translated using Weblate (Ukrainian) Currently translated at 100.0% (78 of 78 strings) Co-authored-by: Profitroll Translate-URL: https://weblate.end-play.xyz/projects/garbagereminder/telegrambot/uk/ Translation: GarbageReminder/TelegramBot --- locale/uk.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/locale/uk.json b/locale/uk.json index 0d46718..275f792 100644 --- a/locale/uk.json +++ b/locale/uk.json @@ -42,9 +42,9 @@ "help": "🔔 Цей бот надсилає сповіщення про вивезення сміття згідно з Вашим місцевим графіком.\n\n**Доступні команди**\n/help - Показати це повідомлення\n/setup - Вибрати місце розташування\n/toggle - Увімкнути/вимкнути нагадування\n/set_time - Встановити час нагадувань\n/set_offset - Встановити зсув між нагадуваннями та вивозом\n/upcoming - Показати майбутні вивезення\n/language - Обрати мову бота\n/checkout - Експортувати або видалити дані\n\nВи також можете запропонувати додати своє місто/район до бота, зв'язавшись з адміністраторами за [цим посиланням]({url_contact}) та вказавши свій розклад.\n\n⚙️ Бажаєте розмістити цього бота самостійно або внести деякі зміни? Бот має відкритий вихідний код, тож ви можете форкнути його. Ознайомтесь із [репозиторієм бота]({url_repo}) щоб дізнатись деталі.\n\nПриємного користування! 🤗", "import_finished": "Ви успішно вставили {count} записів.", "import_invalid_date": "Записи містять невірні формати дат. Використовуйте формат дати **ISO 8601**.", - "import_invalid_filetype": "Неправильний ввід. Будь ласка, надішліть мені JSON-файл із записами. {cancel_notice}", - "import_invalid": "Це недійсний JSON даних збору сміття.", - "import": "Гаразд. Надішліть правильний JSON.", + "import_invalid_filetype": "Неправильний ввід. Будь ласка, надішліть мені JSON або CSV файл із записами. {cancel_notice}", + "import_invalid": "Це недійсний файл з даними про збір сміття.", + "import": "Гаразд. Надішліть мені правильний файл. Він може бути у форматі JSON або CSV. Дізнайтеся більше про підтримувані формати в документації", "locale_choice": "Гаразд. Будь ласка, оберіть мову за допомогою клавіатури нижче.", "location_empty": "У Вас не встановлено локацію вивозу. Оберіть свою локацію за допомогою /setup.", "location_name_empty": "Не вдалося знайти жодного населеного пункту з такою назвою. Спробуйте перефразувати назву або переконайтеся, що Ви використовуєте ту саму мову та назву, що й місцева влада у графіку вивезення сміття.\n\n{cancel_notice}", From 50f80b177f32561afa9905b012c1f594e329ee40 Mon Sep 17 00:00:00 2001 From: Weblate Date: Sun, 29 Oct 2023 19:55:40 +0200 Subject: [PATCH 33/36] Translated using Weblate (German) Currently translated at 100.0% (78 of 78 strings) Co-authored-by: Profitroll Translate-URL: https://weblate.end-play.xyz/projects/garbagereminder/telegrambot/de/ Translation: GarbageReminder/TelegramBot --- locale/de.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/locale/de.json b/locale/de.json index 9c84eda..7d39960 100644 --- a/locale/de.json +++ b/locale/de.json @@ -42,9 +42,9 @@ "help": "🔔 Dieser Bot sendet Ihnen Benachrichtigungen über die Müllabfuhr nach Ihrem lokalen Zeitplan.\n\n**Verfügbare Kommandos**\n/help - Diese Menü anzeigen\n/setup - Standort wählen\n/toggle - Deaktivieren/Aktivieren der Benachrichtigungen\n/set_time - Einstellen der Erinnerungszeit\n/set_offset - Offset zwischen Erinnerung und Abholung einstellen\n/upcoming - Zeigt die nächste Abholung an\n/language - Die Sprache des Bots wählen\n/checkout - Daten exportieren oder löschen\n\n💭 Sie können auch vorschlagen, Ihre Stadt/Ihren Bezirk in den Bot aufzunehmen, indem Sie die Administratoren über [diesen Link]({url_contact}) kontaktieren und Ihren Zeitplan angeben.\n\n⚙️ Möchten Sie diesen Bot selbst hosten oder Änderungen vornehmen? Er ist Open-Source, Sie können ihn also forken. Werfen Sie einen Blick auf das [Bot-Repository]({url_repo}) für Details.\n\nViel Spaß beim Benutzen! 🤗", "import_finished": "Sie haben erfolgreich {count} Einträge eingefügt.", "import_invalid_date": "Die Einträge enthalten ungültige Datumsformate. Verwenden Sie das Datumsformat **ISO 8601**.", - "import_invalid_filetype": "Ungültige Eingabe. Bitte senden Sie mir eine JSON-Datei mit Einträgen. {cancel_notice}", - "import_invalid": "Dies ist kein gültiges Abfallterminen JSON.", - "import": "Okay. Senden Sie mir ein gültiges JSON.", + "import_invalid_filetype": "Ungültige Eingabe. Bitte senden Sie mir eine JSON- oder CSV-Datei mit Einträgen. {cancel_notice}", + "import_invalid": "Dies ist keine gültige Abfalltermine-Datei.", + "import": "Okay. Senden Sie mir eine gültige Datei. Sie kann im JSON- oder CSV-Format sein. Lesen Sie mehr über die unterstützten Formate in der Dokumentation", "locale_choice": "Prima. Bitte wählen Sie die Sprache mit der Tastatur unten.", "location_empty": "Sie haben keinen Standort festgelegt. Verwenden Sie /setup, um Ihren Standort auszuwählen.", "location_name_empty": "Es konnten keine Orte mit diesem Namen gefunden werden. Versuchen Sie, ihn umzuformulieren, oder stellen Sie sicher, dass Sie dieselbe Sprache und denselben Namen verwenden, wie er von Ihren örtlichen Behörden im Müllabfuhrplan angegeben ist.\n\n{cancel_notice}", @@ -95,4 +95,4 @@ "callbacks": { "locale_set": "Ihre Sprache ist jetzt: {locale}" } -} \ No newline at end of file +} From 4e41eb2f2da3a68557aae975f7be43e6ffab01f8 Mon Sep 17 00:00:00 2001 From: Weblate Date: Sun, 29 Oct 2023 19:55:40 +0200 Subject: [PATCH 34/36] Translated using Weblate (Russian) Currently translated at 100.0% (78 of 78 strings) Co-authored-by: Profitroll Translate-URL: https://weblate.end-play.xyz/projects/garbagereminder/telegrambot/ru/ Translation: GarbageReminder/TelegramBot --- locale/ru.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/locale/ru.json b/locale/ru.json index 27adcea..4c2a819 100644 --- a/locale/ru.json +++ b/locale/ru.json @@ -18,9 +18,9 @@ "help": "🔔 Цей бот надсилає сповіщення про вивезення сміття згідно з Вашим місцевим графіком.\n\n**Доступні команди**\n/help - Показати це повідомлення\n/setup - Вибрати місце розташування\n/toggle - Увімкнути/вимкнути нагадування\n/set_time - Встановити час нагадувань\n/set_offset - Встановити зсув між нагадуваннями та вивозом\n/upcoming - Показати майбутні вивезення\n/language - Обрати мову бота\n/checkout - Експортувати або видалити дані\n\nВи також можете запропонувати додати своє місто/район до бота, зв'язавшись з адміністраторами за [цим посиланням]({url_contact}) та вказавши свій розклад.\n\n⚙️ Бажаєте розмістити цього бота самостійно або внести деякі зміни? Бот має відкритий вихідний код, тож ви можете форкнути його. Ознайомтесь із [репозиторієм бота]({url_repo}) щоб дізнатись деталі.\n\nПриємного користування! 🤗", "import_finished": "Ви успішно вставили {count} записів.", "import_invalid_date": "Записи містять невірні формати дат. Використовуйте формат дати **ISO 8601**.", - "import_invalid_filetype": "Неправильний ввід. Будь ласка, надішліть мені JSON-файл із записами. {cancel_notice}", - "import_invalid": "Це недійсний JSON даних збору сміття.", - "import": "Гаразд. Надішліть правильний JSON.", + "import_invalid_filetype": "Неправильний ввід. Будь ласка, надішліть мені JSON або CSV файл із записами. {cancel_notice}", + "import_invalid": "Це недійсний файл з даними про збір сміття.", + "import": "Гаразд. Надішліть мені правильний файл. Він може бути у форматі JSON або CSV. Дізнайтеся більше про підтримувані формати в документації", "locale_choice": "Гаразд. Будь ласка, оберіть мову за допомогою клавіатури нижче.", "location_empty": "У Вас не встановлено локацію вивозу. Оберіть свою локацію за допомогою /setup.", "location_name_empty": "Не вдалося знайти жодного населеного пункту з такою назвою. Спробуйте перефразувати назву або переконайтеся, що Ви використовуєте ту саму мову та назву, що й місцева влада у графіку вивезення сміття.\n\n{cancel_notice}", @@ -95,4 +95,4 @@ "set_offset": "Кількість днів", "set_time": "Час у вигляді ГГ:ХХ" } -} \ No newline at end of file +} From 116a18eba1556499ede9fb87cca51b48dc2797c3 Mon Sep 17 00:00:00 2001 From: profitroll Date: Sun, 5 Nov 2023 13:20:01 +0000 Subject: [PATCH 35/36] [WIP] Database rework --- classes/importer/csv.py | 2 +- classes/importer/json.py | 2 +- classes/location.py | 2 +- classes/pyroclient.py | 2 +- config_example.json | 9 ++++++++- modules/database.py | 4 +--- modules/database_api.py | 29 +++++++++++++++++++++++++++++ modules/reminder.py | 2 +- modules/search_name.py | 2 +- modules/search_nearby.py | 2 +- plugins/commands/upcoming.py | 2 +- 11 files changed, 46 insertions(+), 12 deletions(-) create mode 100644 modules/database_api.py diff --git a/classes/importer/csv.py b/classes/importer/csv.py index c876f92..f406135 100644 --- a/classes/importer/csv.py +++ b/classes/importer/csv.py @@ -6,7 +6,7 @@ from typing import Any, Dict, List, Union from bson import ObjectId from classes.importer.abstract import Importer -from modules.database import col_entries +from modules.database_api import col_entries class ImporterCSV(Importer): diff --git a/classes/importer/json.py b/classes/importer/json.py index 86afaad..798a301 100644 --- a/classes/importer/json.py +++ b/classes/importer/json.py @@ -5,7 +5,7 @@ from bson import ObjectId from ujson import loads from classes.importer.abstract import Importer -from modules.database import col_entries +from modules.database_api import col_entries class ImporterJSON(Importer): diff --git a/classes/location.py b/classes/location.py index 5f51a90..5a29557 100644 --- a/classes/location.py +++ b/classes/location.py @@ -6,7 +6,7 @@ from pytz import timezone as pytz_timezone from pytz.tzinfo import BaseTzInfo, DstTzInfo from classes.point import Point -from modules.database import col_locations +from modules.database_api import col_locations @dataclass diff --git a/classes/pyroclient.py b/classes/pyroclient.py index db580c8..adbcf21 100644 --- a/classes/pyroclient.py +++ b/classes/pyroclient.py @@ -7,7 +7,7 @@ from pyrogram.types import User from classes.location import Location from classes.pyrouser import PyroUser -from modules.database import col_locations +from modules.database_api import col_locations from modules.reminder import remind diff --git a/config_example.json b/config_example.json index cff73b1..3302d10 100644 --- a/config_example.json +++ b/config_example.json @@ -14,7 +14,14 @@ "password": null, "host": "127.0.0.1", "port": 27017, - "name": "garbagebot" + "name": "garbage_bot" + }, + "database_api": { + "user": null, + "password": null, + "host": "127.0.0.1", + "port": 27017, + "name": "garbage_reminder" }, "search": { "radius": 0.1 diff --git a/modules/database.py b/modules/database.py index f85e739..03e589b 100644 --- a/modules/database.py +++ b/modules/database.py @@ -1,4 +1,4 @@ -"""Module that provides all database collections""" +"""Module that provides bot's database collections.""" from typing import Any, Mapping @@ -24,5 +24,3 @@ db_client = AsyncClient(con_string) db: AsyncDatabase = db_client.get_database(name=db_config["name"]) col_users: AsyncCollection = db.get_collection("users") -col_entries: AsyncCollection = db.get_collection("entries") -col_locations: AsyncCollection = db.get_collection("locations") diff --git a/modules/database_api.py b/modules/database_api.py new file mode 100644 index 0000000..fcbf92a --- /dev/null +++ b/modules/database_api.py @@ -0,0 +1,29 @@ +"""Module that provides API database collections. +It's possible to use REST API client instead, but +using MongoDB directly is MUCH faster this way.""" + +from typing import Any, Mapping + +from async_pymongo import AsyncClient, AsyncCollection, AsyncDatabase +from libbot.sync import config_get + +db_config: Mapping[str, Any] = config_get("database_api") + +if db_config["user"] is not None and db_config["password"] is not None: + con_string = "mongodb://{0}:{1}@{2}:{3}/{4}".format( + db_config["user"], + db_config["password"], + db_config["host"], + db_config["port"], + db_config["name"], + ) +else: + con_string = "mongodb://{0}:{1}/{2}".format( + db_config["host"], db_config["port"], db_config["name"] + ) + +db_client = AsyncClient(con_string) +db: AsyncDatabase = db_client.get_database(name=db_config["name"]) + +col_entries: AsyncCollection = db.get_collection("entries") +col_locations: AsyncCollection = db.get_collection("locations") diff --git a/modules/reminder.py b/modules/reminder.py index e4b5794..f408d9a 100644 --- a/modules/reminder.py +++ b/modules/reminder.py @@ -7,7 +7,7 @@ from libbot.pyrogram.classes import PyroClient from classes.enums import GarbageType from classes.location import Location from classes.pyrouser import PyroUser -from modules.database import col_entries, col_users +from modules.database_api import col_entries, col_users from modules.utils import from_utc logger = logging.getLogger(__name__) diff --git a/modules/search_name.py b/modules/search_name.py index 5fae746..534d726 100644 --- a/modules/search_name.py +++ b/modules/search_name.py @@ -6,7 +6,7 @@ from pyrogram.types import ForceReply, Message, ReplyKeyboardRemove from classes.location import Location from classes.pyroclient import PyroClient -from modules.database import col_locations +from modules.database_api import col_locations async def search_name(app: PyroClient, message: Message) -> Union[Location, None]: diff --git a/modules/search_nearby.py b/modules/search_nearby.py index 32def59..29604a3 100644 --- a/modules/search_nearby.py +++ b/modules/search_nearby.py @@ -6,7 +6,7 @@ from pyrogram.types import Message, ReplyKeyboardRemove from classes.location import Location from classes.pyroclient import PyroClient -from modules.database import col_locations +from modules.database_api import col_locations from modules.search_name import search_name diff --git a/plugins/commands/upcoming.py b/plugins/commands/upcoming.py index 9c67396..881958d 100644 --- a/plugins/commands/upcoming.py +++ b/plugins/commands/upcoming.py @@ -6,7 +6,7 @@ from pyrogram.types import Message from classes.garbage_entry import GarbageEntry from classes.pyroclient import PyroClient from modules import custom_filters -from modules.database import col_entries +from modules.database_api import col_entries @PyroClient.on_message( From 3cff52c56dbebe83e425568926e76dfe230e7745 Mon Sep 17 00:00:00 2001 From: profitroll Date: Sun, 5 Nov 2023 13:26:10 +0000 Subject: [PATCH 36/36] Fixed users import --- modules/reminder.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/reminder.py b/modules/reminder.py index f408d9a..e2151e0 100644 --- a/modules/reminder.py +++ b/modules/reminder.py @@ -7,7 +7,8 @@ from libbot.pyrogram.classes import PyroClient from classes.enums import GarbageType from classes.location import Location from classes.pyrouser import PyroUser -from modules.database_api import col_entries, col_users +from modules.database import col_users +from modules.database_api import col_entries from modules.utils import from_utc logger = logging.getLogger(__name__)