forked from sh-edraft.de/sh_discord_bot
		
	Added api calls #70
This commit is contained in:
		| @@ -45,6 +45,7 @@ | ||||
|     ], | ||||
|     "PackageData": {}, | ||||
|     "ProjectReferences": [ | ||||
|       "../bot_api/bot-api.json", | ||||
|       "../bot_core/bot-core.json", | ||||
|       "../bot_data/bot-data.json", | ||||
|       "../modules/base/base.json", | ||||
|   | ||||
| @@ -152,5 +152,13 @@ | ||||
|     "database": {}, | ||||
|     "permission": { | ||||
|     } | ||||
|   }, | ||||
|   "api": { | ||||
|     "api": { | ||||
|       "test_mail": { | ||||
|         "subject": "Krümmelmonster Web Interface Test-Mail", | ||||
|         "message": "Dies ist eine Test-Mail vom Krümmelmonster Web Interface\nGesendet von {}-{}" | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @@ -1 +1,26 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
|  | ||||
| """ | ||||
| bot Keksdose bot | ||||
| ~~~~~~~~~~~~~~~~~~~ | ||||
|  | ||||
| Discord bot  for the Keksdose discord Server | ||||
|  | ||||
| :copyright: (c) 2022 sh-edraft.de | ||||
| :license: MIT, see LICENSE for more details. | ||||
|  | ||||
| """ | ||||
|  | ||||
| __title__ = 'bot_api' | ||||
| __author__ = 'Sven Heidemann' | ||||
| __license__ = 'MIT' | ||||
| __copyright__ = 'Copyright (c) 2022 sh-edraft.de' | ||||
| __version__ = '0.2.3' | ||||
|  | ||||
| from collections import namedtuple | ||||
|  | ||||
|  | ||||
| # imports:  | ||||
|  | ||||
| VersionInfo = namedtuple('VersionInfo', 'major minor micro') | ||||
| version_info = VersionInfo(major='0', minor='2', micro='3') | ||||
|   | ||||
							
								
								
									
										26
									
								
								src/bot_api/abc/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/bot_api/abc/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
|  | ||||
| """ | ||||
| bot Keksdose bot | ||||
| ~~~~~~~~~~~~~~~~~~~ | ||||
|  | ||||
| Discord bot  for the Keksdose discord Server | ||||
|  | ||||
| :copyright: (c) 2022 sh-edraft.de | ||||
| :license: MIT, see LICENSE for more details. | ||||
|  | ||||
| """ | ||||
|  | ||||
| __title__ = 'bot_api.abc' | ||||
| __author__ = 'Sven Heidemann' | ||||
| __license__ = 'MIT' | ||||
| __copyright__ = 'Copyright (c) 2022 sh-edraft.de' | ||||
| __version__ = '0.2.3' | ||||
|  | ||||
| from collections import namedtuple | ||||
|  | ||||
|  | ||||
| # imports: | ||||
|  | ||||
| VersionInfo = namedtuple('VersionInfo', 'major minor micro') | ||||
| version_info = VersionInfo(major='0', minor='2', micro='3') | ||||
							
								
								
									
										13
									
								
								src/bot_api/abc/dto_abc.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/bot_api/abc/dto_abc.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| from abc import ABC, abstractmethod | ||||
|  | ||||
|  | ||||
| class DtoABC(ABC): | ||||
|  | ||||
|     @abstractmethod | ||||
|     def __init__(self): pass | ||||
|  | ||||
|     @abstractmethod | ||||
|     def from_dict(self, values: dict): pass | ||||
|  | ||||
|     @abstractmethod | ||||
|     def to_dict(self) -> dict: pass | ||||
| @@ -1,6 +1,9 @@ | ||||
| import os | ||||
|  | ||||
| from cpl_core.configuration import ConfigurationABC | ||||
| from cpl_core.dependency_injection import ServiceCollectionABC | ||||
| from cpl_core.environment import ApplicationEnvironmentABC | ||||
| from cpl_core.mailing import EMailClientABC, EMailClient | ||||
| from cpl_discord.service.discord_collection_abc import DiscordCollectionABC | ||||
| from flask import Flask | ||||
|  | ||||
| @@ -17,9 +20,16 @@ class ApiModule(ModuleABC): | ||||
|         ModuleABC.__init__(self, dc, FeatureFlagsEnum.api_module) | ||||
|  | ||||
|     def configure_configuration(self, config: ConfigurationABC, env: ApplicationEnvironmentABC): | ||||
|         pass | ||||
|         cwd = env.working_directory | ||||
|         env.set_working_directory(os.path.dirname(os.path.realpath(__file__))) | ||||
|         config.add_json_file(f'config/apisettings.json', optional=False) | ||||
|         config.add_json_file(f'config/apisettings.{env.environment_name}.json', optional=True) | ||||
|         config.add_json_file(f'config/apisettings.{env.host_name}.json', optional=True) | ||||
|         env.set_working_directory(cwd) | ||||
|  | ||||
|     def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC): | ||||
|         services.add_singleton(EMailClientABC, EMailClient) | ||||
|  | ||||
|         services.add_singleton(ApiThread) | ||||
|         services.add_singleton(Flask, Api) | ||||
|  | ||||
|   | ||||
							
								
								
									
										8
									
								
								src/bot_api/config/apisettings.development.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/bot_api/config/apisettings.development.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| { | ||||
|   "EMailClientSettings": { | ||||
|     "Host": "mail.sh-edraft.de", | ||||
|     "Port": "587", | ||||
|     "UserName": "dev-srv@sh-edraft.de", | ||||
|     "Credentials": "RmBOQX1eNFYiYjgsSid3fV1nelc2WA==" | ||||
|   } | ||||
| } | ||||
							
								
								
									
										1
									
								
								src/bot_api/config/apisettings.edrafts-lapi.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/bot_api/config/apisettings.edrafts-lapi.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| {} | ||||
							
								
								
									
										1
									
								
								src/bot_api/config/apisettings.edrafts-pc-ubuntu.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/bot_api/config/apisettings.edrafts-pc-ubuntu.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| {} | ||||
							
								
								
									
										1
									
								
								src/bot_api/config/apisettings.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/bot_api/config/apisettings.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| {} | ||||
							
								
								
									
										1
									
								
								src/bot_api/config/apisettings.production.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/bot_api/config/apisettings.production.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| {} | ||||
							
								
								
									
										1
									
								
								src/bot_api/config/apisettings.staging.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/bot_api/config/apisettings.staging.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| {} | ||||
							
								
								
									
										1
									
								
								src/bot_api/config/appsettings.PC-Nick.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/bot_api/config/appsettings.PC-Nick.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| {} | ||||
							
								
								
									
										1
									
								
								src/bot_api/configuration/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/bot_api/configuration/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| # imports | ||||
							
								
								
									
										23
									
								
								src/bot_api/configuration/api_settings.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/bot_api/configuration/api_settings.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| import traceback | ||||
|  | ||||
| from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC | ||||
| from cpl_core.console import Console | ||||
|  | ||||
|  | ||||
| class APISettings(ConfigurationModelABC): | ||||
|  | ||||
|     def __init__(self): | ||||
|         ConfigurationModelABC.__init__(self) | ||||
|  | ||||
|         self._redirect_to_https = False | ||||
|  | ||||
|     @property | ||||
|     def redirect_to_https(self) -> bool: | ||||
|         return self._redirect_to_https | ||||
|  | ||||
|     def from_dict(self, settings: dict): | ||||
|         try: | ||||
|             self._redirect_to_https = bool(settings['RedirectToHTTPS']) | ||||
|         except Exception as e: | ||||
|             Console.error(f'[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings') | ||||
|             Console.error(f'[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}') | ||||
							
								
								
									
										55
									
								
								src/bot_api/configuration/version_settings.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								src/bot_api/configuration/version_settings.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | ||||
| from typing import Optional | ||||
|  | ||||
| from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC | ||||
| from cpl_cli.configuration.version_settings_name_enum import VersionSettingsNameEnum | ||||
|  | ||||
|  | ||||
| class VersionSettings(ConfigurationModelABC): | ||||
|  | ||||
|     def __init__( | ||||
|             self, | ||||
|             major: str = None, | ||||
|             minor: str = None, | ||||
|             micro: str = None | ||||
|     ): | ||||
|         ConfigurationModelABC.__init__(self) | ||||
|  | ||||
|         self._major: Optional[str] = major | ||||
|         self._minor: Optional[str] = minor | ||||
|         self._micro: Optional[str] = micro | ||||
|  | ||||
|     @property | ||||
|     def major(self) -> str: | ||||
|         return self._major | ||||
|  | ||||
|     @property | ||||
|     def minor(self) -> str: | ||||
|         return self._minor | ||||
|  | ||||
|     @property | ||||
|     def micro(self) -> str: | ||||
|         return self._micro | ||||
|  | ||||
|     def to_str(self) -> str: | ||||
|         if self._micro is None: | ||||
|             return f'{self._major}.{self._minor}' | ||||
|         else: | ||||
|             return f'{self._major}.{self._minor}.{self._micro}' | ||||
|  | ||||
|     def from_dict(self, settings: dict): | ||||
|         self._major = settings[VersionSettingsNameEnum.major.value] | ||||
|         self._minor = settings[VersionSettingsNameEnum.minor.value] | ||||
|         micro = settings[VersionSettingsNameEnum.micro.value] | ||||
|         if micro != '': | ||||
|             self._micro = micro | ||||
|  | ||||
|     def to_dict(self) -> dict: | ||||
|         version = { | ||||
|             VersionSettingsNameEnum.major.value: self._major, | ||||
|             VersionSettingsNameEnum.minor.value: self._minor, | ||||
|         } | ||||
|  | ||||
|         if self._micro is not None: | ||||
|             version[VersionSettingsNameEnum.micro.value] = self._micro | ||||
|  | ||||
|         return version | ||||
| @@ -0,0 +1,26 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
|  | ||||
| """ | ||||
| bot Keksdose bot | ||||
| ~~~~~~~~~~~~~~~~~~~ | ||||
|  | ||||
| Discord bot  for the Keksdose discord Server | ||||
|  | ||||
| :copyright: (c) 2022 sh-edraft.de | ||||
| :license: MIT, see LICENSE for more details. | ||||
|  | ||||
| """ | ||||
|  | ||||
| __title__ = 'bot_api.controller' | ||||
| __author__ = 'Sven Heidemann' | ||||
| __license__ = 'MIT' | ||||
| __copyright__ = 'Copyright (c) 2022 sh-edraft.de' | ||||
| __version__ = '0.2.3' | ||||
|  | ||||
| from collections import namedtuple | ||||
|  | ||||
|  | ||||
| # imports: | ||||
|  | ||||
| VersionInfo = namedtuple('VersionInfo', 'major minor micro') | ||||
| version_info = VersionInfo(major='0', minor='2', micro='3') | ||||
|   | ||||
| @@ -1,7 +1,14 @@ | ||||
| import os | ||||
|  | ||||
| from cpl_core.configuration import ConfigurationABC | ||||
| from cpl_core.environment import ApplicationEnvironmentABC | ||||
| from cpl_core.mailing import EMail, EMailClientABC, EMailClientSettings | ||||
| from cpl_translation import TranslatePipe | ||||
|  | ||||
| from bot_api.api import Api | ||||
| from bot_api.logging.api_logger import ApiLogger | ||||
| from bot_api.model.settings_dto import SettingsDTO | ||||
| from bot_api.model.version_dto import VersionDTO | ||||
| from bot_api.route.route import Route | ||||
|  | ||||
|  | ||||
| @@ -9,14 +16,57 @@ class ApiController: | ||||
|  | ||||
|     def __init__( | ||||
|             self, | ||||
|             config: ConfigurationABC, | ||||
|             env: ApplicationEnvironmentABC, | ||||
|             logger: ApiLogger, | ||||
|             t: TranslatePipe, | ||||
|             api: Api | ||||
|             api: Api, | ||||
|             mail_settings: EMailClientSettings, | ||||
|             mailer: EMailClientABC | ||||
|     ): | ||||
|         self._config = config | ||||
|         self._env = env | ||||
|         self._logger = logger | ||||
|         self._t = t | ||||
|         self._api = api | ||||
|         self._mail_settings = mail_settings | ||||
|         self._mailer = mailer | ||||
|  | ||||
|     @Route.route('/api/hello-world') | ||||
|     def hello_world(self): | ||||
|         return self._t.transform('common.hello_world') | ||||
|     @Route.route('/api/api-version') | ||||
|     def api_version(self): | ||||
|         import bot_api | ||||
|         version = bot_api.version_info | ||||
|         return VersionDTO(version.major, version.minor, version.micro).to_dict() | ||||
|  | ||||
|     @Route.route('/api/settings') | ||||
|     def settings(self): | ||||
|         # TODO: Authentication | ||||
|         import bot_api | ||||
|         version = bot_api.version_info | ||||
|  | ||||
|         return SettingsDTO( | ||||
|             '', | ||||
|             VersionDTO(version.major, version.minor, version.micro), | ||||
|             os.path.abspath(os.path.join(self._env.working_directory, 'config')), | ||||
|             '', | ||||
|             '/', | ||||
|             0, | ||||
|             0, | ||||
|             self._mail_settings.user_name, | ||||
|             self._mail_settings.port, | ||||
|             self._mail_settings.host, | ||||
|             self._mail_settings.user_name, | ||||
|             self._mail_settings.user_name, | ||||
|         ).to_dict() | ||||
|  | ||||
|     @Route.route('/api/send-test-mail/<email>') | ||||
|     def send_test_mail(self, email: str): | ||||
|         # TODO: Authentication | ||||
|         mail = EMail() | ||||
|         mail.add_header('Mime-Version: 1.0') | ||||
|         mail.add_header('Content-Type: text/plain; charset=utf-8') | ||||
|         mail.add_header('Content-Transfer-Encoding: quoted-printable') | ||||
|         mail.add_receiver(email) | ||||
|         mail.subject = self._t.transform('api.api.test_mail.subject') | ||||
|         mail.body = self._t.transform('api.api.test_mail.message').format(self._env.host_name, self._env.environment_name) | ||||
|         self._mailer.send_mail(mail) | ||||
|   | ||||
| @@ -0,0 +1,26 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
|  | ||||
| """ | ||||
| bot Keksdose bot | ||||
| ~~~~~~~~~~~~~~~~~~~ | ||||
|  | ||||
| Discord bot  for the Keksdose discord Server | ||||
|  | ||||
| :copyright: (c) 2022 sh-edraft.de | ||||
| :license: MIT, see LICENSE for more details. | ||||
|  | ||||
| """ | ||||
|  | ||||
| __title__ = 'bot_api.logging' | ||||
| __author__ = 'Sven Heidemann' | ||||
| __license__ = 'MIT' | ||||
| __copyright__ = 'Copyright (c) 2022 sh-edraft.de' | ||||
| __version__ = '0.2.3' | ||||
|  | ||||
| from collections import namedtuple | ||||
|  | ||||
|  | ||||
| # imports: | ||||
|  | ||||
| VersionInfo = namedtuple('VersionInfo', 'major minor micro') | ||||
| version_info = VersionInfo(major='0', minor='2', micro='3') | ||||
|   | ||||
							
								
								
									
										26
									
								
								src/bot_api/model/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/bot_api/model/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
|  | ||||
| """ | ||||
| bot Keksdose bot | ||||
| ~~~~~~~~~~~~~~~~~~~ | ||||
|  | ||||
| Discord bot  for the Keksdose discord Server | ||||
|  | ||||
| :copyright: (c) 2022 sh-edraft.de | ||||
| :license: MIT, see LICENSE for more details. | ||||
|  | ||||
| """ | ||||
|  | ||||
| __title__ = 'bot_api.model' | ||||
| __author__ = 'Sven Heidemann' | ||||
| __license__ = 'MIT' | ||||
| __copyright__ = 'Copyright (c) 2022 sh-edraft.de' | ||||
| __version__ = '0.2.3' | ||||
|  | ||||
| from collections import namedtuple | ||||
|  | ||||
|  | ||||
| # imports: | ||||
|  | ||||
| VersionInfo = namedtuple('VersionInfo', 'major minor micro') | ||||
| version_info = VersionInfo(major='0', minor='2', micro='3') | ||||
							
								
								
									
										71
									
								
								src/bot_api/model/settings_dto.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								src/bot_api/model/settings_dto.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,71 @@ | ||||
| import traceback | ||||
|  | ||||
| from cpl_core.console import Console | ||||
|  | ||||
| from bot_api.abc.dto_abc import DtoABC | ||||
| from bot_api.model.version_dto import VersionDTO | ||||
|  | ||||
|  | ||||
| class SettingsDTO(DtoABC): | ||||
|  | ||||
|     def __init__( | ||||
|             self, | ||||
|             web_version: str, | ||||
|             api_version: VersionDTO, | ||||
|             config_path: str, | ||||
|             web_base_url: str, | ||||
|             api_base_url: str, | ||||
|             token_expire_time: int, | ||||
|             refresh_token_expire_time: int, | ||||
|             mail_user: str, | ||||
|             mail_port: int, | ||||
|             mail_host: str, | ||||
|             mail_transceiver: str, | ||||
|             mail_transceiver_address: str, | ||||
|     ): | ||||
|         DtoABC.__init__(self) | ||||
|  | ||||
|         self._web_version = '' | ||||
|         self._api_version = VersionDTO() | ||||
|         self._config_path = '' | ||||
|         self._web_base_url = '' | ||||
|         self._api_base_url = '' | ||||
|  | ||||
|         self._token_expire_time = 0 | ||||
|         self._refresh_token_expire_time = 0 | ||||
|  | ||||
|         self._mail_user = '' | ||||
|         self._mail_port = 0 | ||||
|         self._mail_host = '' | ||||
|         self._mail_transceiver = '' | ||||
|         self._mail_transceiver_address = '' | ||||
|  | ||||
|     def from_dict(self, values: dict): | ||||
|         self._web_version = values['WebVersion'] | ||||
|         self._api_version.from_dict(values['ApiVersion']) | ||||
|         self._config_path = values['ConfigPath'] | ||||
|         self._web_base_url = values['WebBaseURL'] | ||||
|         self._api_base_url = values['ApiBaseURL'] | ||||
|         self._token_expire_time = values['TokenExpireTime'] | ||||
|         self._refresh_token_expire_time = values['RefreshTokenExpireTime'] | ||||
|         self._mail_user = values['MailUser'] | ||||
|         self._mail_port = values['MailPort'] | ||||
|         self._mail_host = values['MailHost'] | ||||
|         self._mail_transceiver = values['MailTransceiver'] | ||||
|         self._mail_transceiver_address = values['MailTransceiverAddress'] | ||||
|  | ||||
|     def to_dict(self) -> dict: | ||||
|         return { | ||||
|             'WebVersion': self._web_version, | ||||
|             'ApiVersion': self._api_version.to_dict(), | ||||
|             'ConfigPath': self._config_path, | ||||
|             'WebBaseURL': self._web_base_url, | ||||
|             'ApiBaseURL': self._api_base_url, | ||||
|             'TokenExpireTime': self._token_expire_time, | ||||
|             'RefreshTokenExpireTime': self._refresh_token_expire_time, | ||||
|             'MailUser': self._mail_user, | ||||
|             'MailPort': self._mail_port, | ||||
|             'MailHost': self._mail_host, | ||||
|             'MailTransceiver': self._mail_transceiver, | ||||
|             'MailTransceiverAddress': self._mail_transceiver_address, | ||||
|         } | ||||
							
								
								
									
										27
									
								
								src/bot_api/model/version_dto.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/bot_api/model/version_dto.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| import traceback | ||||
|  | ||||
| from cpl_core.console import Console | ||||
|  | ||||
| from bot_api.abc.dto_abc import DtoABC | ||||
|  | ||||
|  | ||||
| class VersionDTO(DtoABC): | ||||
|  | ||||
|     def __init__(self, major: str = None, minor: str = None, micro: str = None): | ||||
|         DtoABC.__init__(self) | ||||
|  | ||||
|         self._major = major | ||||
|         self._minor = minor | ||||
|         self._micro = micro | ||||
|  | ||||
|     def from_dict(self, values: dict): | ||||
|         self._major = values['Major'] | ||||
|         self._minor = values['Minor'] | ||||
|         self._micro = values['Micro'] | ||||
|  | ||||
|     def to_dict(self) -> dict: | ||||
|         return { | ||||
|             'Major': self._major, | ||||
|             'Minor': self._minor, | ||||
|             'Micro': self._micro, | ||||
|         } | ||||
| @@ -0,0 +1,26 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
|  | ||||
| """ | ||||
| bot Keksdose bot | ||||
| ~~~~~~~~~~~~~~~~~~~ | ||||
|  | ||||
| Discord bot  for the Keksdose discord Server | ||||
|  | ||||
| :copyright: (c) 2022 sh-edraft.de | ||||
| :license: MIT, see LICENSE for more details. | ||||
|  | ||||
| """ | ||||
|  | ||||
| __title__ = 'bot_api.route' | ||||
| __author__ = 'Sven Heidemann' | ||||
| __license__ = 'MIT' | ||||
| __copyright__ = 'Copyright (c) 2022 sh-edraft.de' | ||||
| __version__ = '0.2.3' | ||||
|  | ||||
| from collections import namedtuple | ||||
|  | ||||
|  | ||||
| # imports: | ||||
|  | ||||
| VersionInfo = namedtuple('VersionInfo', 'major minor micro') | ||||
| version_info = VersionInfo(major='0', minor='2', micro='3') | ||||
|   | ||||
		Reference in New Issue
	
	Block a user