9 Commits

12 changed files with 171 additions and 19 deletions

21
classes/api_client.py Normal file
View File

@@ -0,0 +1,21 @@
from garbage_api_client import Client
from garbage_api_client.api.default.entry_find_locations_location_entries_get import (
asyncio as entry_find,
)
from garbage_api_client.api.default.location_find_locations_get import (
asyncio as location_find,
)
from garbage_api_client.api.default.location_get_locations_id_get import (
asyncio as location_get,
)
class APIClient(Client):
async def entry_find(self, *args, **kwargs):
await entry_find(client=self, *args, **kwargs)
async def location_find(self, *args, **kwargs):
await location_find(client=self, *args, **kwargs)
async def location_get(self, *args, **kwargs):
await location_get(client=self, *args, **kwargs)

View File

@@ -1,24 +1,43 @@
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
from pyrogram.types import User
from classes.api_client import APIClient
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, 2)
super().__init__(**kwargs)
self.updater = Updater(ClientSession())
self.contexts = []
self.api_client = APIClient()
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 +50,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 +91,24 @@ class PyroClient(LibPyroClient):
return [
await Location.get(record["id"]) async for record in col_locations.find({})
]
async def check_updates(self) -> None:
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"],
),
)

44
classes/updater.py Normal file
View File

@@ -0,0 +1,44 @@
import logging
from typing import Any, Dict, Tuple
from aiohttp import ClientSession
logger = logging.getLogger(__name__)
class Updater:
def __init__(self, client_session: ClientSession) -> None:
self.client_session: ClientSession = client_session
async def check_updates(
self, version_current: Tuple[int, int, int], api_url: str
) -> bool:
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]:
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

View File

@@ -9,6 +9,9 @@
"max_concurrent_transmissions": 1,
"scoped_commands": true
},
"api": {
"url": "https://api.garbagebot.eu"
},
"database": {
"user": null,
"password": null,
@@ -16,13 +19,6 @@
"port": 27017,
"name": "garbage_bot"
},
"database_api": {
"user": null,
"password": null,
"host": "127.0.0.1",
"port": 27017,
"name": "garbage_reminder"
},
"search": {
"radius": 0.1
},
@@ -32,6 +28,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
}

View File

@@ -78,7 +78,8 @@
"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",

View File

@@ -78,7 +78,8 @@
"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",

View File

@@ -78,7 +78,8 @@
"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": "Я погоджуюсь і хочу продовжити",

View File

@@ -78,7 +78,8 @@
"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": "Я погоджуюсь і хочу продовжити",

View File

@@ -35,14 +35,16 @@ with contextlib.suppress(ImportError):
def main():
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)
if args.migrate:
migrate_database()
try:
client.run()
except KeyboardInterrupt:

View 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")

View File

@@ -0,0 +1,21 @@
from libbot import sync
from mongodb_migrations.base import BaseMigration
class Migration(BaseMigration):
def upgrade(self):
sync.config_set("api", {"url": "https://api.garbagebot.eu"})
sync.config_delete("database_api", missing_ok=True)
def downgrade(self):
sync.config_delete("api", missing_ok=True)
sync.config_set(
"database_api",
{
"user": None,
"password": None,
"host": "127.0.0.1",
"port": 27017,
"name": "garbage_reminder",
},
)

View File

@@ -7,5 +7,7 @@ ujson>=5.0.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]==3.0.0
pykeyboard==0.1.7
libbot[speed,pyrogram]==3.2.2
pykeyboard==0.1.7
--extra-index-url https://git.end-play.xyz/api/packages/GarbageReminder/pypi/simple
garbageapi-client==0.1.0