Compare commits
53 Commits
Author | SHA1 | Date | |
---|---|---|---|
a5824dbd6b | |||
f8472b1b3f | |||
13be95f0f8 | |||
524087f59f | |||
cc66cc386b | |||
39f7904bdc | |||
b5173a8dba | |||
0a06e8493f | |||
c2ee35b3d9 | |||
896262b83e | |||
f6b1749408 | |||
6867b64a18 | |||
f42117e542 | |||
a5a513cb82 | |||
3fef2eb028 | |||
11ca3223ab | |||
6b138126c1 | |||
e6adb03f61 | |||
d51fa1e04c | |||
8b2456c2fd | |||
6a6b4cd6cd | |||
852f4307f8 | |||
e73797d819 | |||
bfd99a44a6 | |||
d078ab37d8 | |||
ea0ab6443f | |||
04ee8e9c60 | |||
2c15bbb4d2 | |||
99d621d90f | |||
dc389ac1b7 | |||
3f20fdb46a | |||
65e9e830c1 | |||
1c76c8d911 | |||
e307d60e8e | |||
0562521f0d | |||
7293cafd2e | |||
b5bfbcd375 | |||
de483cd450 | |||
94e229949d | |||
b7fc1715fd | |||
f6731d5734 | |||
e1a7b6309e | |||
2404ee9095 | |||
3bd4f794d3 | |||
bebd6b4e4f | |||
fd52c8f74e | |||
b2f09339ee | |||
95a9b5cb2b | |||
b7779fffd0 | |||
e3e9ec0cc8 | |||
6efb20781b | |||
0ef066ffa1 | |||
ff63743421 |
@ -10,7 +10,7 @@ class CallbackLanguage:
|
||||
language: str
|
||||
|
||||
@classmethod
|
||||
def from_callback(cls, callback: CallbackQuery):
|
||||
def from_callback(cls, callback: CallbackQuery) -> "CallbackLanguage":
|
||||
"""Parse callback query and extract language data from it.
|
||||
|
||||
### Args:
|
||||
|
@ -23,7 +23,7 @@ class GarbageEntry:
|
||||
date: datetime
|
||||
|
||||
@classmethod
|
||||
async def from_dict(cls, data: Mapping[str, Any]):
|
||||
async def from_dict(cls, data: Mapping[str, Any]) -> "GarbageEntry":
|
||||
"""Generate GarbageEntry object from the mapping provided
|
||||
|
||||
### Args:
|
||||
@ -60,7 +60,7 @@ class GarbageEntry:
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def from_record(cls, data: Mapping[str, Any]):
|
||||
async def from_record(cls, data: Mapping[str, Any]) -> "GarbageEntry":
|
||||
locations = [
|
||||
await Location.get(location_id) for location_id in data["locations"]
|
||||
]
|
||||
|
@ -28,7 +28,7 @@ class Location:
|
||||
timezone: Union[BaseTzInfo, DstTzInfo]
|
||||
|
||||
@classmethod
|
||||
async def get(cls, id: int):
|
||||
async def get(cls, id: int) -> "Location":
|
||||
db_entry = await col_locations.find_one({"id": id})
|
||||
|
||||
if db_entry is None:
|
||||
@ -40,7 +40,7 @@ class Location:
|
||||
return cls(**db_entry)
|
||||
|
||||
@classmethod
|
||||
async def find(cls, name: str):
|
||||
async def find(cls, name: str) -> "Location":
|
||||
db_entry = await col_locations.find_one({"name": {"$regex": name}})
|
||||
|
||||
if db_entry is None:
|
||||
@ -52,7 +52,7 @@ class Location:
|
||||
return cls(**db_entry)
|
||||
|
||||
@classmethod
|
||||
async def nearby(cls, lat: float, lon: float):
|
||||
async def nearby(cls, lat: float, lon: float) -> "Location":
|
||||
db_entry = await col_locations.find_one({"location": {"$near": [lon, lat]}})
|
||||
|
||||
if db_entry is None:
|
||||
|
@ -1,5 +1,8 @@
|
||||
import logging
|
||||
from datetime import datetime, timedelta
|
||||
from typing import List, Union
|
||||
|
||||
from aiohttp import ClientSession
|
||||
from apscheduler.triggers.cron import CronTrigger
|
||||
from libbot.pyrogram.classes import PyroClient as LibPyroClient
|
||||
from pymongo import ASCENDING, GEOSPHERE, TEXT
|
||||
@ -7,18 +10,32 @@ from pyrogram.types import User
|
||||
|
||||
from classes.location import Location
|
||||
from classes.pyrouser import PyroUser
|
||||
from classes.updater import Updater
|
||||
from modules.database_api import col_locations
|
||||
from modules.reminder import remind
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class PyroClient(LibPyroClient):
|
||||
def __init__(self, **kwargs):
|
||||
self.__version__ = (0, 1, 3)
|
||||
|
||||
super().__init__(**kwargs)
|
||||
|
||||
self.updater = Updater()
|
||||
self.contexts = []
|
||||
|
||||
if self.scheduler is not None:
|
||||
self.scheduler.add_job(
|
||||
remind, CronTrigger.from_crontab("* * * * *"), args=(self,)
|
||||
)
|
||||
self.contexts = []
|
||||
if self.config["update_checker"]:
|
||||
self.scheduler.add_job(
|
||||
self.check_updates,
|
||||
CronTrigger.from_crontab("0 12 */3 * *"),
|
||||
next_run_time=datetime.now() + timedelta(seconds=10),
|
||||
)
|
||||
|
||||
async def start(self, **kwargs):
|
||||
await col_locations.create_index(
|
||||
@ -31,6 +48,10 @@ class PyroClient(LibPyroClient):
|
||||
await col_locations.create_index([("name", TEXT)], name="location_name")
|
||||
return await super().start(**kwargs)
|
||||
|
||||
async def stop(self, **kwargs):
|
||||
await self.updater.client_session.close()
|
||||
await super().stop(**kwargs)
|
||||
|
||||
async def find_user(self, user: Union[int, User]) -> PyroUser:
|
||||
"""Find User by it's ID or User object.
|
||||
|
||||
@ -68,3 +89,25 @@ class PyroClient(LibPyroClient):
|
||||
return [
|
||||
await Location.get(record["id"]) async for record in col_locations.find({})
|
||||
]
|
||||
|
||||
async def check_updates(self) -> None:
|
||||
"""Check for updates and send a message to the owner if newer version was found"""
|
||||
if await self.updater.check_updates(
|
||||
self.__version__, self.config["strings"]["url_updater"]
|
||||
):
|
||||
try:
|
||||
release = await self.updater.get_latest_release(
|
||||
self.config["strings"]["url_updater"]
|
||||
)
|
||||
except Exception as exc:
|
||||
logger.error("Could not fetch the latest version: %s", exc)
|
||||
return
|
||||
|
||||
await self.send_message(
|
||||
self.owner,
|
||||
self._("update_available", "messages").format(
|
||||
version_current=f"v{'.'.join(str(subversion) for subversion in self.__version__)}",
|
||||
version_new=release["tag_name"],
|
||||
release_url=release["html_url"],
|
||||
),
|
||||
)
|
||||
|
@ -1,7 +1,9 @@
|
||||
import logging
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Union
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Any, Mapping, Tuple, Union
|
||||
|
||||
import pytz
|
||||
from bson import ObjectId
|
||||
|
||||
from classes.location import Location
|
||||
@ -42,9 +44,9 @@ class PyroUser:
|
||||
enabled: bool = True,
|
||||
location_id: int = 0,
|
||||
offset: int = 1,
|
||||
time_hour: int = 18,
|
||||
time_hour: int = 16,
|
||||
time_minute: int = 0,
|
||||
):
|
||||
) -> "PyroUser":
|
||||
db_entry = await col_users.find_one({"id": id})
|
||||
|
||||
if db_entry is None:
|
||||
@ -72,7 +74,7 @@ class PyroUser:
|
||||
return cls(**db_entry)
|
||||
|
||||
@classmethod
|
||||
async def from_dict(cls, **kwargs):
|
||||
async def from_dict(cls, **kwargs) -> "PyroUser":
|
||||
if "location" in kwargs:
|
||||
try:
|
||||
kwargs["location"] = await Location.get(kwargs["location"]) # type: ignore
|
||||
@ -80,48 +82,193 @@ class PyroUser:
|
||||
kwargs["location"] = None # type: ignore
|
||||
return cls(**kwargs)
|
||||
|
||||
async def update_locale(self, locale: Union[str, None]) -> None:
|
||||
async def update_locale(self, locale: Union[str, None]) -> Union[str, None]:
|
||||
"""Change user's locale stored in the database.
|
||||
|
||||
### Args:
|
||||
* locale (`Union[str, None]`): New locale to be set.
|
||||
"""
|
||||
logger.debug("%s's locale has been set to %s", self.id, locale)
|
||||
logger.info("%s's locale has been set to %s", self.id, locale)
|
||||
|
||||
await col_users.update_one({"_id": self._id}, {"$set": {"locale": locale}})
|
||||
|
||||
async def update_state(self, enabled: bool = False) -> None:
|
||||
logger.debug("%s's state has been set to %s", self.id, enabled)
|
||||
self.locale = locale
|
||||
|
||||
return self.locale
|
||||
|
||||
async def update_state(self, enabled: bool = False) -> bool:
|
||||
"""Update user's state (enabled/disabled)
|
||||
|
||||
### Args:
|
||||
* enabled (`bool`, *optional*): Whether the user is enabled. Defaults to `False`.
|
||||
|
||||
### Returns:
|
||||
* `bool`: User's current state
|
||||
"""
|
||||
logger.info("%s's state has been set to %s", self.id, enabled)
|
||||
|
||||
await col_users.update_one({"_id": self._id}, {"$set": {"enabled": enabled}})
|
||||
|
||||
async def update_location(self, location_id: int = 0) -> None:
|
||||
logger.debug("%s's location has been set to %s", self.id, location_id)
|
||||
self.enabled = enabled
|
||||
|
||||
return self.enabled
|
||||
|
||||
async def update_location(self, location_id: int) -> Location:
|
||||
"""Update user's location and move their time to the new timezone (if the user had a location set previously)
|
||||
|
||||
### Args:
|
||||
* location_id (`int`): ID of the location
|
||||
|
||||
### Returns:
|
||||
`Location`: New location
|
||||
"""
|
||||
logger.info("%s's location has been set to %s", self.id, location_id)
|
||||
|
||||
await col_users.update_one(
|
||||
{"_id": self._id}, {"$set": {"location": location_id}}
|
||||
)
|
||||
|
||||
async def update_offset(self, offset: int = 1) -> None:
|
||||
logger.debug("%s's offset has been set to %s", self.id, offset)
|
||||
location = await Location.get(location_id)
|
||||
|
||||
# Execute if timezones of old and new locations are different
|
||||
if self.location and (self.location.timezone.zone != location.timezone.zone):
|
||||
# Get UTC time for selected reminder time
|
||||
now_utc = datetime.now(pytz.utc).replace(
|
||||
hour=self.time_hour, minute=self.time_minute, second=0, microsecond=0
|
||||
)
|
||||
|
||||
# Get the time for the reminder time of old and new location
|
||||
local_old = now_utc.astimezone(self.location.timezone)
|
||||
local_new = (
|
||||
location.timezone.localize(local_old.replace(tzinfo=None))
|
||||
).astimezone(pytz.utc)
|
||||
|
||||
# Update the time to match the new timezone
|
||||
await self.update_time(hour=local_new.hour, minute=local_new.minute)
|
||||
|
||||
self.location = location
|
||||
|
||||
return self.location
|
||||
|
||||
async def update_offset(self, offset: int = 1) -> int:
|
||||
"""Update the offset of the reminder (in days)
|
||||
|
||||
### Args:
|
||||
* offset (`int`, *optional*): Offset in days. Defaults to `1`.
|
||||
|
||||
### Returns:
|
||||
* `int`: Offset in days
|
||||
"""
|
||||
logger.info("%s's offset has been set to %s", self.id, offset)
|
||||
|
||||
await col_users.update_one({"_id": self._id}, {"$set": {"offset": offset}})
|
||||
|
||||
async def update_time(self, hour: int = 18, minute: int = 0) -> None:
|
||||
logger.debug("%s's time has been set to %s h. %s m.", self.id, hour, minute)
|
||||
self.offset = offset
|
||||
|
||||
return offset
|
||||
|
||||
async def update_time(self, hour: int = 16, minute: int = 0) -> Tuple[int, int]:
|
||||
"""Update the time of the reminder (hour and minute, for UTC timezone)
|
||||
|
||||
### Args:
|
||||
* hour (`int`, *optional*): Hour of the reminder. Defaults to `16`.
|
||||
* minute (`int`, *optional*): Minute of the reminder. Defaults to `0`.
|
||||
|
||||
### Returns:
|
||||
* `Tuple[int, int]`: Hour and minute of the reminder
|
||||
"""
|
||||
logger.info("%s's time has been set to %s h. %s m.", self.id, hour, minute)
|
||||
|
||||
await col_users.update_one(
|
||||
{"_id": self._id}, {"$set": {"time_hour": hour, "time_minute": minute}}
|
||||
)
|
||||
|
||||
self.time_hour = hour
|
||||
self.time_minute = minute
|
||||
|
||||
return self.time_hour, self.time_minute
|
||||
|
||||
async def delete(self) -> None:
|
||||
logger.debug("%s's data has been deleted", self.id)
|
||||
"""Delete the database record of the user"""
|
||||
logger.info("%s's data has been deleted", self.id)
|
||||
|
||||
await col_users.delete_one({"_id": self._id})
|
||||
|
||||
async def checkout(self) -> Any:
|
||||
logger.debug("%s's data has been checked out", self.id)
|
||||
async def checkout(self) -> Mapping[str, Any]:
|
||||
"""Checkout the user's database record
|
||||
|
||||
### Raises:
|
||||
* `KeyError`: Database record of the user was not found
|
||||
|
||||
### Returns:
|
||||
* `Mapping[str, Any]`: Database record
|
||||
"""
|
||||
logger.info("%s's data has been checked out", self.id)
|
||||
|
||||
db_entry = await col_users.find_one({"_id": self._id})
|
||||
|
||||
if db_entry is None:
|
||||
raise KeyError(
|
||||
f"DB record with id {self._id} of user {self.id} is not found"
|
||||
f"DB record with id {self._id} of user {self.id} was not found"
|
||||
)
|
||||
|
||||
del db_entry["_id"] # type: ignore
|
||||
|
||||
return db_entry
|
||||
|
||||
def get_reminder_date(self) -> datetime:
|
||||
"""Get next reminder date (year, month and day)
|
||||
|
||||
### Raises:
|
||||
* `AttributeError`: Some attribute(s) are missing
|
||||
|
||||
### Returns:
|
||||
* `datetime`: Datetime object of the next reminder date
|
||||
"""
|
||||
if self.location is None:
|
||||
logger.warning(
|
||||
"Could not get the reminder date for %s: User does not have some attribute(s) set",
|
||||
self.id,
|
||||
)
|
||||
raise AttributeError(
|
||||
f"Could not get the reminder date for {self.id}: User does not have some attribute(s) set"
|
||||
)
|
||||
|
||||
if not self.location.timezone:
|
||||
logger.warning("Location %s does not have a timezone set", self.location.id)
|
||||
|
||||
return (
|
||||
datetime.now(self.location.timezone or pytz.utc) + timedelta(days=1)
|
||||
).replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
|
||||
def get_reminder_time(self) -> datetime:
|
||||
"""Get reminder time (hour and minute)
|
||||
|
||||
### Raises:
|
||||
* `AttributeError`: Some attribute(s) are missing
|
||||
|
||||
### Returns:
|
||||
* `datetime`: Datetime object of the next reminder date
|
||||
"""
|
||||
if self.time_hour is None or self.time_minute is None or self.location is None:
|
||||
logger.warning(
|
||||
"Could not get the reminder time for %s: User does not have some attribute(s) set",
|
||||
self.id,
|
||||
)
|
||||
raise AttributeError(
|
||||
f"Could not get the reminder time for {self.id}: User does not have some attribute(s) set"
|
||||
)
|
||||
|
||||
if not self.location.timezone:
|
||||
logger.warning("Location %s does not have a timezone set", self.location.id)
|
||||
|
||||
return (
|
||||
datetime.now(pytz.utc)
|
||||
.replace(
|
||||
hour=self.time_hour,
|
||||
minute=self.time_minute,
|
||||
second=0,
|
||||
microsecond=0,
|
||||
)
|
||||
.astimezone(self.location.timezone or pytz.utc)
|
||||
)
|
||||
|
50
classes/updater.py
Normal file
50
classes/updater.py
Normal file
@ -0,0 +1,50 @@
|
||||
import logging
|
||||
from typing import Any, Dict, Tuple, Union
|
||||
|
||||
from aiohttp import ClientSession
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Updater:
|
||||
def __init__(self, client_session: Union[ClientSession, None] = None) -> None:
|
||||
self.client_session: Union[ClientSession, None] = client_session
|
||||
|
||||
async def check_updates(
|
||||
self, version_current: Tuple[int, int, int], api_url: str
|
||||
) -> bool:
|
||||
if not self.client_session:
|
||||
self.client_session = ClientSession()
|
||||
|
||||
response = await self.client_session.get(api_url)
|
||||
|
||||
if response.status != 200:
|
||||
return False
|
||||
|
||||
try:
|
||||
version_latest = (await response.json())["tag_name"][1:].split(".")
|
||||
except Exception as exc:
|
||||
logger.error("Error parsing latest version: %s", exc)
|
||||
return False
|
||||
|
||||
return any(
|
||||
version_current[index] < int(subversion)
|
||||
for index, subversion in enumerate(version_latest)
|
||||
)
|
||||
|
||||
async def get_latest_release(self, api_url: str) -> Dict[str, Any]:
|
||||
if not self.client_session:
|
||||
self.client_session = ClientSession()
|
||||
|
||||
response = await self.client_session.get(api_url)
|
||||
|
||||
if response.status != 200:
|
||||
raise RuntimeError(f"Could not fetch latest release: {response.status}")
|
||||
|
||||
try:
|
||||
return await response.json()
|
||||
except Exception as exc:
|
||||
logger.error("Error parsing latest release: %s", exc)
|
||||
raise RuntimeError(
|
||||
f"Error parsing latest release: {response.status}"
|
||||
) from exc
|
@ -32,6 +32,8 @@
|
||||
"disabled_plugins": [],
|
||||
"strings": {
|
||||
"url_repo": "https://git.end-play.xyz/GarbageReminder/TelegramBot",
|
||||
"url_contact": "https://git.end-play.xyz/GarbageReminder/TelegramBot/issues"
|
||||
}
|
||||
"url_contact": "https://git.end-play.xyz/GarbageReminder/TelegramBot/issues",
|
||||
"url_updater": "https://git.end-play.xyz/api/v1/repos/GarbageReminder/TelegramBot/releases/latest"
|
||||
},
|
||||
"update_checker": true
|
||||
}
|
@ -8,7 +8,7 @@
|
||||
},
|
||||
"bot": {
|
||||
"name": "Garbage Reminder",
|
||||
"about": "Nie wieder Müllabfuhrtermin verpassen. Quellcode: https://garbagebot.eu",
|
||||
"about": "Nie wieder Müllabfuhrtermin verpassen. Mehr erfahren: https://garbagebot.eu",
|
||||
"description": "Sie können Erinnerungen an die Müllabfuhr für Orte Ihrer Wahl erhalten.\n\nVerwenden Sie /help, um die Funktionsweise des Bots besser zu verstehen, oder verwenden Sie /setup, um Ihre Erinnerungen zu konfigurieren."
|
||||
},
|
||||
"formats": {
|
||||
@ -64,21 +64,22 @@
|
||||
"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_finished": "✅ Fertig! Sie werden die Erinnerungen an die Müllabfuhr {offset} T. im Voraus um {time} für **{name}** 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.",
|
||||
"start_selection_yes": "✅ Fertig! Sie werden die Erinnerungen an die Müllabfuhr {offset} T. im Voraus um {time} für **{name}** erhalten.\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 [Nutzungsbedingungen]({terms_of_service}) und [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": "Keine Müllabfuhr-Einträge für die nächsten 30 Tage bei **{name}** gefunden",
|
||||
"upcoming": "Bevorstehende Müllabfuhr:\n\n{entries}"
|
||||
"upcoming": "Bevorstehende Müllabfuhr:\n\n{entries}",
|
||||
"update_available": "Es gibt eine neue Version von GarbageBot!\n\nVersion: `{version_current}` -> `{version_new}`\n\n[Release-Seite]({release_url}) | [Update-Anleitung](https://garbagebot.eu/bot_telegram/upgrading)"
|
||||
},
|
||||
"force_replies": {
|
||||
"import": "JSON mit Abfalltermine",
|
||||
@ -99,4 +100,4 @@
|
||||
"callbacks": {
|
||||
"locale_set": "Ihre Sprache ist jetzt: {locale}"
|
||||
}
|
||||
}
|
||||
}
|
@ -8,7 +8,7 @@
|
||||
},
|
||||
"bot": {
|
||||
"name": "Garbage Reminder",
|
||||
"about": "Never forget about garbage collection again. Source code: https://garbagebot.eu",
|
||||
"about": "Never forget about garbage collection again. Learn more: https://garbagebot.eu",
|
||||
"description": "You can receive reminders about garbage collection for locations of your choice.\n\nUse /help to better understand how the bot works or use /setup to configure your reminders."
|
||||
},
|
||||
"formats": {
|
||||
@ -64,21 +64,22 @@
|
||||
"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_finished": "✅ Finished! You will receive reminders about garbage collection {offset} d. in advance at {time} for **{name}**.",
|
||||
"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.",
|
||||
"start_selection_yes": "✅ Finished! You will receive reminders about garbage collection {offset} d. in advance at {time} for **{name}**.\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 [Terms of service]({terms_of_service}) and [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": "No garbage collection entries found for the next 30 days at **{name}**",
|
||||
"upcoming": "Upcoming garbage collection:\n\n{entries}"
|
||||
"upcoming": "Upcoming garbage collection:\n\n{entries}",
|
||||
"update_available": "There is a new version of GarbageBot available!\n\nVersion: `{version_current}` -> `{version_new}`\n\n[Release page]({release_url}) | [Update instructions](https://garbagebot.eu/bot_telegram/upgrading)"
|
||||
},
|
||||
"buttons": {
|
||||
"delete_confirm": "I agree and want to proceed",
|
||||
|
@ -8,7 +8,7 @@
|
||||
},
|
||||
"bot": {
|
||||
"name": "Garbage Reminder 🇺🇦",
|
||||
"about": "Більше ніколи не забувайте про вивезення сміття. Вихідний код: https://garbagebot.eu",
|
||||
"about": "Більше ніколи не забувайте про вивезення сміття. Дізнатись більше: https://garbagebot.eu",
|
||||
"description": "Ви можете отримувати нагадування про вивезення сміття для обраних вами місць.\n\nВикористовуйте /help, щоб краще зрозуміти, як працює бот, або /setup, щоб налаштувати нагадування."
|
||||
},
|
||||
"formats": {
|
||||
@ -64,21 +64,22 @@
|
||||
"set_time_finished": "🔔 Час сповіщень було оновлено! Тепер Ви будете отримувати сповіщення про вивіз сміття за {offset} д. до вивозу о **{time}**. {toggle_notice}",
|
||||
"set_time_invalid": "Будь ласка, вкажіть дійсний час у форматі ГГ:ХХ. {cancel_notice}",
|
||||
"set_time": "Гаразд. Будь ласка, надішліть бажаний час у форматі ГГ:ХХ.",
|
||||
"setup_finished": "✅ Готово! Ваше місцезнаходження тепер **{name}**. Ви будете отримувати сповіщення про вивезення сміття за {offset} д. заздалегідь о {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Тепер офіційна частина закінчена, тож Ви можете зануритися в бота.",
|
||||
"start_selection_yes": "✅ Готово! Ви будете отримувати нагадування про вивезення сміття для **{name}** за {offset} д. заздалегідь о {time}.\n\nБудь ласка, скористайтесь /help, якщо Ви хочете дізнатися, як змінити час сповіщень або вимкнути їх.",
|
||||
"start": "👋 Вітання!\n\nЦей невеличкий бот з відкритим вихідним кодом створений для того, щоб трохи спростити Вам життя, надсилаючи сповіщення про вивіз сміття у вашому регіоні.\n\nКористуючись цим ботом, Ви приймаєте [Умови надання послуг]({terms_of_service}) та [Політику конфіденційності]({privacy_policy}), в іншому випадку, будь ласка, заблокуйте та видаліть цього бота перед подальшою взаємодією.\n\nТепер офіційна частина закінчена, тож Ви можете зануритися в бота.",
|
||||
"toggle_disabled": "🔕 Сповіщення було вимкнено.",
|
||||
"toggle_enabled_location": "🔔 Сповіщення увімкнено за {offset} д. до вивезення сміття о {time} для локації **{name}**.",
|
||||
"toggle_enabled": "🔔 Сповіщення було увімкнено за {offset} д. до вивезення сміття о {time}. Оберіть своє розташування за допомогою /setup.",
|
||||
"toggle": "Використовуйте /toggle, щоб увімкнути сповіщення.",
|
||||
"upcoming_empty": "Не знайдено записів про вивезення сміття на найближчі 30 днів для **{name}**",
|
||||
"upcoming": "Найближчі вивози сміття:\n\n{entries}"
|
||||
"upcoming": "Найближчі вивози сміття:\n\n{entries}",
|
||||
"update_available": "Доступна нова версія GarbageBot!\n\nВерсія: `{version_current}` -> `{version_new}`\n\n[Сторінка релізу]({release_url}) | [Інструкція з оновлення](https://garbagebot.eu/bot_telegram/upgrading)"
|
||||
},
|
||||
"buttons": {
|
||||
"delete_confirm": "Я погоджуюсь і хочу продовжити",
|
||||
@ -99,4 +100,4 @@
|
||||
"set_offset": "Кількість днів",
|
||||
"set_time": "Час у вигляді ГГ:ХХ"
|
||||
}
|
||||
}
|
||||
}
|
@ -8,7 +8,7 @@
|
||||
},
|
||||
"bot": {
|
||||
"name": "Garbage Reminder 🇺🇦",
|
||||
"about": "Більше ніколи не забувайте про вивезення сміття. Вихідний код: https://garbagebot.eu",
|
||||
"about": "Більше ніколи не забувайте про вивезення сміття. Дізнатись більше: https://garbagebot.eu",
|
||||
"description": "Ви можете отримувати нагадування про вивезення сміття для обраних вами місць.\n\nВикористовуйте /help, щоб краще зрозуміти, як працює бот, або /setup, щоб налаштувати нагадування."
|
||||
},
|
||||
"formats": {
|
||||
@ -64,21 +64,22 @@
|
||||
"set_time_finished": "🔔 Час сповіщень було оновлено! Тепер Ви будете отримувати сповіщення про вивіз сміття за {offset} д. до вивозу о **{time}**. {toggle_notice}",
|
||||
"set_time_invalid": "Будь ласка, вкажіть дійсний час у форматі ГГ:ХХ. {cancel_notice}",
|
||||
"set_time": "Гаразд. Будь ласка, надішліть бажаний час у форматі ГГ:ХХ.",
|
||||
"setup_finished": "✅ Готово! Ваше місцезнаходження тепер **{name}**. Ви будете отримувати сповіщення про вивезення сміття за {offset} д. заздалегідь о {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Тепер офіційна частина закінчена, тож Ви можете зануритися в бота.",
|
||||
"start_selection_yes": "✅ Готово! Ви будете отримувати нагадування про вивезення сміття для **{name}** за {offset} д. заздалегідь о {time}.\n\nБудь ласка, скористайтесь /help, якщо Ви хочете дізнатися, як змінити час сповіщень або вимкнути їх.",
|
||||
"start": "👋 Вітання!\n\nЦей невеличкий бот з відкритим вихідним кодом створений для того, щоб трохи спростити Вам життя, надсилаючи сповіщення про вивіз сміття у вашому регіоні.\n\nКористуючись цим ботом, Ви приймаєте [Умови надання послуг]({terms_of_service}) та [Політику конфіденційності]({privacy_policy}), в іншому випадку, будь ласка, заблокуйте та видаліть цього бота перед подальшою взаємодією.\n\nТепер офіційна частина закінчена, тож Ви можете зануритися в бота.",
|
||||
"toggle_disabled": "🔕 Сповіщення було вимкнено.",
|
||||
"toggle_enabled_location": "🔔 Сповіщення увімкнено за {offset} д. до вивезення сміття о {time} для локації **{name}**.",
|
||||
"toggle_enabled": "🔔 Сповіщення було увімкнено за {offset} д. до вивезення сміття о {time}. Оберіть своє розташування за допомогою /setup.",
|
||||
"toggle": "Використовуйте /toggle, щоб увімкнути сповіщення.",
|
||||
"upcoming_empty": "Не знайдено записів про вивезення сміття на найближчі 30 днів для **{name}**",
|
||||
"upcoming": "Найближчі вивози сміття:\n\n{entries}"
|
||||
"upcoming": "Найближчі вивози сміття:\n\n{entries}",
|
||||
"update_available": "Доступна нова версія GarbageBot!\n\nВерсія: `{version_current}` -> `{version_new}`\n\n[Сторінка релізу]({release_url}) | [Інструкція з оновлення](https://garbagebot.eu/bot_telegram/upgrading)"
|
||||
},
|
||||
"buttons": {
|
||||
"delete_confirm": "Я погоджуюсь і хочу продовжити",
|
||||
@ -99,4 +100,4 @@
|
||||
"set_offset": "Кількість днів",
|
||||
"set_time": "Час у вигляді ГГ:ХХ"
|
||||
}
|
||||
}
|
||||
}
|
14
main.py
14
main.py
@ -4,6 +4,7 @@ from argparse import ArgumentParser
|
||||
from os import getpid
|
||||
from pathlib import Path
|
||||
|
||||
from aiohttp import ClientSession
|
||||
from convopyro import Conversation
|
||||
from libbot import sync
|
||||
|
||||
@ -35,13 +36,16 @@ with contextlib.suppress(ImportError):
|
||||
|
||||
|
||||
def main():
|
||||
client = PyroClient(
|
||||
scheduler=scheduler, commands_source=sync.json_read(Path("commands.json"))
|
||||
)
|
||||
Conversation(client)
|
||||
|
||||
if args.migrate:
|
||||
migrate_database()
|
||||
logger.info("Migration finished. Exiting...")
|
||||
exit()
|
||||
|
||||
client = PyroClient(
|
||||
scheduler=scheduler,
|
||||
commands_source=sync.json_read(Path("commands.json")),
|
||||
)
|
||||
Conversation(client)
|
||||
|
||||
try:
|
||||
client.run()
|
||||
|
16
migrations/202405261500.py
Normal file
16
migrations/202405261500.py
Normal file
@ -0,0 +1,16 @@
|
||||
from libbot import sync
|
||||
from mongodb_migrations.base import BaseMigration
|
||||
|
||||
|
||||
class Migration(BaseMigration):
|
||||
def upgrade(self):
|
||||
sync.config_set("update_checker", True)
|
||||
sync.config_set(
|
||||
"url_updater",
|
||||
"https://git.end-play.xyz/api/v1/repos/GarbageReminder/TelegramBot/releases/latest",
|
||||
"strings",
|
||||
)
|
||||
|
||||
def downgrade(self):
|
||||
sync.config_delete("update_checker", missing_ok=True)
|
||||
sync.config_delete("url_updater", "strings", missing_ok=True)
|
@ -1,6 +1,7 @@
|
||||
import logging
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import datetime
|
||||
|
||||
import pytz
|
||||
from bson import json_util
|
||||
from libbot.pyrogram.classes import PyroClient
|
||||
|
||||
@ -9,13 +10,12 @@ from classes.location import Location
|
||||
from classes.pyrouser import PyroUser
|
||||
from modules.database import col_users
|
||||
from modules.database_api import col_entries
|
||||
from modules.utils import from_utc
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def remind(app: PyroClient) -> None:
|
||||
utcnow = datetime.utcnow()
|
||||
utcnow = datetime.now(pytz.utc)
|
||||
|
||||
logger.debug("Performing reminder lookup for %s (UTCNOW)", utcnow)
|
||||
|
||||
@ -40,12 +40,10 @@ async def remind(app: PyroClient) -> None:
|
||||
try:
|
||||
location: Location = await app.get_location(user.location.id) # type: ignore
|
||||
except ValueError:
|
||||
logger.warning("Skipping reminder for %s due to invalid location", user.id)
|
||||
continue
|
||||
|
||||
user_date = from_utc(
|
||||
datetime.utcnow() + timedelta(days=user.offset),
|
||||
user.location.timezone.zone,
|
||||
).replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
user_date = user.get_reminder_date().replace(tzinfo=None)
|
||||
|
||||
entries = await col_entries.find(
|
||||
{
|
||||
|
@ -1,12 +1,13 @@
|
||||
from datetime import datetime
|
||||
from typing import Union
|
||||
|
||||
from pytz import UTC
|
||||
from pytz import timezone as pytz_timezone
|
||||
import pytz
|
||||
|
||||
|
||||
def to_utc(date: datetime, timezone: Union[str, None] = None) -> datetime:
|
||||
"""Move timezone unaware datetime object to UTC timezone and return it.
|
||||
"""*DEPRECATED AND WILL BE REMOVED IN FUTURE RELEASES*
|
||||
|
||||
Move timezone unaware datetime object to UTC timezone and return it.
|
||||
|
||||
### Args:
|
||||
* date (`datetime`): Datetime to be converted.
|
||||
@ -16,11 +17,15 @@ def to_utc(date: datetime, timezone: Union[str, None] = None) -> datetime:
|
||||
* `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)
|
||||
return (
|
||||
pytz.timezone(timezone).localize(date).astimezone(pytz.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.
|
||||
"""*DEPRECATED AND WILL BE REMOVED IN FUTURE RELEASES*
|
||||
|
||||
Move timezone unaware datetime object to the timezone specified and return it.
|
||||
|
||||
### Args:
|
||||
* date (`datetime`): Datetime to be converted.
|
||||
@ -31,8 +36,5 @@ def from_utc(date: datetime, timezone: Union[str, None] = None) -> datetime:
|
||||
"""
|
||||
timezone = "UTC" if timezone is None else timezone
|
||||
return (
|
||||
pytz_timezone("UTC")
|
||||
.localize(date)
|
||||
.astimezone(pytz_timezone(timezone))
|
||||
.replace(tzinfo=None)
|
||||
pytz.utc.localize(date).astimezone(pytz.timezone(timezone)).replace(tzinfo=None)
|
||||
)
|
||||
|
@ -1,13 +1,13 @@
|
||||
import logging
|
||||
from datetime import datetime
|
||||
|
||||
import pytz
|
||||
from convopyro import listen_message
|
||||
from pyrogram import filters
|
||||
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__)
|
||||
|
||||
@ -65,18 +65,19 @@ async def command_set_offset(app: PyroClient, message: Message):
|
||||
|
||||
logger.info("User %s has set offset to %s", user.id, offset)
|
||||
|
||||
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"))
|
||||
garbage_time = (
|
||||
datetime.now(pytz.utc)
|
||||
.replace(hour=user.time_hour, minute=user.time_minute)
|
||||
.astimezone(user.location.timezone or pytz.utc)
|
||||
)
|
||||
|
||||
await answer.reply_text(
|
||||
app._("set_offset_finished", "messages", locale=user.locale).format(
|
||||
offset=offset,
|
||||
time=garbage_time,
|
||||
toggle_notice=""
|
||||
if user.enabled
|
||||
else app._("toggle", "messages", locale=user.locale),
|
||||
time=garbage_time.strftime(app._("time", "formats", locale=user.locale)),
|
||||
toggle_notice=(
|
||||
"" if user.enabled else app._("toggle", "messages", locale=user.locale)
|
||||
),
|
||||
),
|
||||
reply_markup=ReplyKeyboardRemove(),
|
||||
)
|
||||
|
@ -1,13 +1,13 @@
|
||||
import logging
|
||||
from datetime import datetime
|
||||
|
||||
import pytz
|
||||
from convopyro import listen_message
|
||||
from pyrogram import filters
|
||||
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__)
|
||||
|
||||
@ -55,31 +55,33 @@ async def command_set_time(app: PyroClient, message: Message):
|
||||
|
||||
break
|
||||
|
||||
now = datetime.now()
|
||||
# Time we got from the user
|
||||
parsed_time = datetime.strptime(answer.text, "%H:%M")
|
||||
|
||||
parsed_time = datetime.strptime(answer.text, "%H:%M").replace(
|
||||
year=now.year, month=now.month, day=now.day, second=0, microsecond=0
|
||||
# Datetime user means in their timezone
|
||||
user_time = datetime.now(user.location.timezone).replace(
|
||||
hour=parsed_time.hour, minute=parsed_time.minute, second=0, microsecond=0
|
||||
)
|
||||
|
||||
user_time = to_utc(parsed_time, user.location.timezone.zone)
|
||||
# Datetime in user's timezone moved to UTC timezone
|
||||
utc_time = user_time.astimezone(pytz.utc)
|
||||
|
||||
await user.update_time(hour=user_time.hour, minute=user_time.minute)
|
||||
await user.update_time(hour=utc_time.hour, minute=utc_time.minute)
|
||||
|
||||
logger.info(
|
||||
"User %s has selected notification time of %s",
|
||||
"User %s has selected notification time of %s (%s UTC)",
|
||||
user.id,
|
||||
user_time.strftime("%H:%M"),
|
||||
utc_time.strftime("%H:%M"),
|
||||
)
|
||||
|
||||
garbage_time = parsed_time.strftime(app._("time", "formats"))
|
||||
|
||||
await answer.reply_text(
|
||||
app._("set_time_finished", "messages", locale=user.locale).format(
|
||||
offset=user.offset,
|
||||
time=garbage_time,
|
||||
toggle_notice=""
|
||||
if user.enabled
|
||||
else app._("toggle", "messages", locale=user.locale),
|
||||
time=user_time.strftime(app._("time", "formats", locale=user.locale)),
|
||||
toggle_notice=(
|
||||
"" if user.enabled else app._("toggle", "messages", locale=user.locale)
|
||||
),
|
||||
),
|
||||
reply_markup=ReplyKeyboardRemove(),
|
||||
)
|
||||
|
@ -1,5 +1,4 @@
|
||||
import logging
|
||||
from datetime import datetime
|
||||
|
||||
from convopyro import listen_message
|
||||
from libbot import i18n
|
||||
@ -11,7 +10,6 @@ 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__)
|
||||
|
||||
@ -74,10 +72,9 @@ async def command_setup(app: PyroClient, message: Message):
|
||||
|
||||
await user.update_location(location.id)
|
||||
|
||||
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))
|
||||
user_time = user.get_reminder_time().strftime(
|
||||
app._("time", "formats", locale=user.locale)
|
||||
)
|
||||
|
||||
await message.reply_text(
|
||||
app._("setup_finished", "messages", locale=user.locale).format(
|
||||
|
@ -1,5 +1,3 @@
|
||||
from datetime import datetime
|
||||
|
||||
from convopyro import listen_message
|
||||
from pyrogram import filters
|
||||
from pyrogram.types import (
|
||||
@ -11,7 +9,6 @@ from pyrogram.types import (
|
||||
|
||||
from classes.pyroclient import PyroClient
|
||||
from modules import custom_filters
|
||||
from modules.utils import from_utc
|
||||
|
||||
|
||||
@PyroClient.on_message(
|
||||
@ -20,7 +17,12 @@ from modules.utils import from_utc
|
||||
async def command_start(app: PyroClient, message: Message):
|
||||
user = await app.find_user(message.from_user)
|
||||
|
||||
await message.reply_text(app._("start", "messages", locale=user.locale))
|
||||
await message.reply_text(
|
||||
app._("start", "messages", locale=user.locale).format(
|
||||
terms_of_service="https://garbagebot.eu/community/public/terms-of-service",
|
||||
privacy_policy="https://garbagebot.eu/community/public/privacy-policy",
|
||||
)
|
||||
)
|
||||
|
||||
join_code = None if len(message.command) == 1 else message.command[1]
|
||||
|
||||
@ -86,13 +88,15 @@ async def command_start(app: PyroClient, message: Message):
|
||||
|
||||
await user.update_location(location.id)
|
||||
|
||||
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))
|
||||
user_time = user.get_reminder_time().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
|
||||
name=location.name,
|
||||
offset=user.offset,
|
||||
time=user_time,
|
||||
),
|
||||
reply_markup=ReplyKeyboardRemove(),
|
||||
)
|
||||
|
@ -1,11 +1,8 @@
|
||||
from datetime import datetime
|
||||
|
||||
from pyrogram import filters
|
||||
from pyrogram.types import Message
|
||||
|
||||
from classes.pyroclient import PyroClient
|
||||
from modules import custom_filters
|
||||
from modules.utils import from_utc
|
||||
|
||||
|
||||
@PyroClient.on_message(
|
||||
@ -16,16 +13,18 @@ async def command_toggle(app: PyroClient, message: Message):
|
||||
|
||||
await user.update_state(not user.enabled)
|
||||
|
||||
if user.enabled:
|
||||
if not user.enabled:
|
||||
await message.reply_text(
|
||||
app._("toggle_disabled", "messages", locale=user.locale)
|
||||
)
|
||||
return
|
||||
|
||||
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"))
|
||||
try:
|
||||
user_time = user.get_reminder_time().strftime(
|
||||
app._("time", "formats", locale=user.locale)
|
||||
)
|
||||
except AttributeError:
|
||||
user_time = "N/A"
|
||||
|
||||
if user.location is None:
|
||||
await message.reply_text(
|
||||
|
@ -1,5 +1,6 @@
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import pytz
|
||||
from pyrogram import filters
|
||||
from pyrogram.types import Message
|
||||
|
||||
@ -23,11 +24,11 @@ async def command_upcoming(app: PyroClient, message: Message):
|
||||
|
||||
date_min = (
|
||||
datetime.now(user.location.timezone).replace(second=0, microsecond=0)
|
||||
).replace(tzinfo=timezone.utc)
|
||||
).replace(tzinfo=pytz.utc)
|
||||
date_max = (
|
||||
datetime.now(user.location.timezone).replace(second=0, microsecond=0)
|
||||
+ timedelta(days=30)
|
||||
).replace(tzinfo=timezone.utc)
|
||||
).replace(tzinfo=pytz.utc)
|
||||
|
||||
entries = [
|
||||
await GarbageEntry.from_record(entry)
|
||||
|
@ -1,11 +1,12 @@
|
||||
aiohttp~=3.9.5
|
||||
aiohttp~=3.10.2
|
||||
apscheduler~=3.10.4
|
||||
async_pymongo==0.1.9
|
||||
convopyro==0.5
|
||||
mongodb-migrations==1.3.0
|
||||
mongodb-migrations==1.3.1
|
||||
pytz>=2024.1
|
||||
tgcrypto==1.2.5
|
||||
ujson>=5.0.0
|
||||
uvloop==0.19.0
|
||||
uvloop==0.20.0
|
||||
--extra-index-url https://git.end-play.xyz/api/packages/profitroll/pypi/simple
|
||||
async_pymongo==0.1.4
|
||||
libbot[speed,pyrogram]==3.0.0
|
||||
libbot[speed,pyrogram]==3.2.3
|
||||
pykeyboard==0.1.7
|
@ -4,6 +4,6 @@
|
||||
"enabled": true,
|
||||
"location": 1,
|
||||
"offset": 1,
|
||||
"time_hour": 18,
|
||||
"time_hour": 16,
|
||||
"time_minute": 0
|
||||
}
|
Reference in New Issue
Block a user