Simple wrapper over aiogram for create bots structure in json files
Project description
JGram
About the project
Jgram was created to be able to quickly and conveniently write telegram bots, placing their structure in .json files Jgram based on AIOgram-2.21 framework
for development info see TODO and CHANGES
Table of contents
installation
From sources
git clone https://github.com/GrehBan/jgram.git
cd jgram
poetry install --no-dev
Speedups
pip install uvloop cchardet aiodns ujson
Getting started
windows.json
{
"locale": "en",
"start": {
"text": "Hello unknown user",
"markup": {
"type": "inline",
"markup": [
[
{
"text": "Register",
"callback_data": "write_name"
}
]
]
}
},
"write_name": {
"text": "Write your name please",
"markup": {
"type": "inline",
"markup": [
[
{
"text": "back",
"callback_data": "start"
}
]
]
},
"next_step": "write_age"
},
"write_age": {
"text": "Your name is {name}\nwrite your age please",
"markup": {
"type": "inline",
"markup": [
[
{
"text": "back",
"callback_data": "write_name"
}
]
]
},
"allowed_updates": ["text"],
"next_step": "save_data"
},
"save_data": {
"text": "Your name is {name}\nYou {age} years old\nThank you!",
"allowed_updates": ["text"]
}
}
import asyncio
import os
from jgram import Registry
from jgram.context import Context
from jgram.manager import WindowsManager
async def name_formatter(update, manager: WindowsManager, context: Context):
context.data['name'] = update.text
async def age_formatter(update, manager: WindowsManager, context: Context):
context.data['age'] = update.text
async def main():
registry = Registry(token=os.getenv('BOT_TOKEN'))
registry.manager.load_windows('windows.json')
registry.register_middleware(name_formatter, name='write_age')
registry.register_middleware(age_formatter, name='save_data')
try:
await registry.start()
finally:
await registry.close()
if __name__ == '__main__':
asyncio.run(main())
Initialization
Registry
from jgram import Registry
registry = Registry()
# initialization arguments
# registry initialize bot and dispatcher from one of this arguments, and must have one of it
bot -> typing.Optional[aiogram.Bot] # current aiogram's bot
dispatcher -> typing.Optional[aiogram.Dispatcher] # current aiogram's dispatcher
token -> typing.Optional[str] # bot's token
# registry initialize new jgram.WindowsManager instance, if manager argument is not provided
manager -> typing.Optional[jgram.WindowsManager] # current manager for manage windows
Windows manager
from jgram import WindowsManager
manager = WindowsManager()
# initialization arguments
# manager initialize new jgram.loader.JsonLoader instance, if loader arguemnt is not provided
loader -> typing.Optional[jgram.loader.protocols.LoaderProto] # current loader for load raw json
# manager initialize new jgram.storage.memory.MemoryStorage, if storage argument is not provided
storage -> typing.Optional[jgram.storage.protocols.BaseStorage] # current users data storage
start_window -> str # name of window than be rendered when /start command is handled
default = "start"
Storage
from jgram.storage.memory import MemoryStorage # memory storage for example
storage = MemoryStorage()
Loader
from jgram.loader import JsonLoader
loader = JsonLoader()
# initialization arguments
default_locale -> typing.Optional[str] # locale which will be used if "locale" field in window is not provided, if set to None, all of windows must have "locale" field
default = 'en'
json_loads -> typing.Callable[..., Any] # function to loads json from string
default = json.loads # python's built-in json's module func
Windows
Structure
{
"lang": "{lang}",
"{text_name}": {
"text": "{text}",
"media": {
"type": "{media_type}",
"url": "{media_url}",
"path": "{media_path}",
"file_id": "{media_file_id}"
}
"markup": {
"type": "{type}",
"markup":
[
[{"text": "Button text", "callback_data": "{next_step callback data}"}],
[{"text": "Url button", "url": "{url}"}],
[{"text": "Reply button"}]
],
"parse_mode": null,
"web_preview": false,
"allowed_updates": ["{update_type}"],
"filters": [
{
"{filter_name}": "{value}",
"next_step": "{value}"
}
],
"next_step": "{value}",
"reset_context": false
}
}
}
Fields
"lang" -> typing.Optional[str] # Locale of windows
"text" -> typing.Optional[str] # bot message text, uses as a caption to media, if it set
"media" -> typing.Optional[typing.Dict] # bot message media
"type" -> str # media type
values = ["photo", "video", "animation", "audio", "document"]
# media must have one of this fields
"url" -> typing.Optional[str] # media url
"path" -> typing.Optional[str] # path to media file
"file_id" -> typing.Optional[str] # media file id
"markup" -> typing.Optional[typing.Dict] # bot message reply markup
"type" -> str # markup type
values = ["inline", "reply"]
"markup" typing.List[typing.List[typing.Dict[str, str]]] # list of lists of markup buttons
"parse_mode" -> typing.Optional[str] # bot message parse mode
values = ["HTML", "MARKDOWN", "MARKDOWN_V2"]
"web_preview" -> bool # bot message disable or enable web preview
default = False
"allowed_updates" -> typing.List[str] # list of content types allowed for processing
default = []
values = ["text", "photo", "video", "animation", "audio", "document"]
"filters" -> typing.List[typing.Dict] # list of dicts than represents aiogram's filters
default = []
"{filter_name}" -> str # Aiogram's filter value
...
"next_step" -> str # name of window than be rendered when filters passed
"next_step" -> typing.Optional[str] # name of window than be rendered if any filter not passed or filters not found
"reset_context" -> bool # reset current user context
default = False
Loading windows
from jgram import WindowsManager
manager = WindowsManager()
manager.load_windows("path/to/file/windows.json")
Middlewares
middlewares call before window rendered, and in middleware you can change user context data or manipulate window processing
middlewares can return a True or False, if returns True, update handler will continue render window, but if returns False, window will not be rendered
for example
from aiogram.types import Message, CallbackQuery
from jgram import Registry
from jgram.context import Context
from jgram.manager import WindowsManager
async def middleware(
update: typing.Union[Message, CallbackQuery],
manager: WindowsManager,
context: Context
):
if isinstance(update, CallbackQuery):
return True # skip middleware processing, and render window
context.data['name'] = update.text # save current message text to "name" field
registry = Registry()
registry.register_middleware(middleware) # middleware will be processed for all windows
# if you can process middleware only for one window
registry.register_middleware(middleware, name="window_name")
Filters
you have two ways to filter update
Aiogram's filters
you can use aiogram's filters, to filter update, but only if filter have key field
for example
{
"filtered": {
"text": "Hello",
"filters": [
{
"chat_id": 123,
"next_step": "chat_123"
}
],
"next_step": "any_another_chat"
}
}
if current update chat id is 123 renders "chat_123" window, in another situations renders "any_another_chat" window
Middleware as filter
you can use jgram's middleware as filter
for example
from aiogram.types import Message, CallbackQuery
from jgram.context import Context
from jgram.manager import WindowsManager
async def middleware(
update: typing.Union[Message, CallbackQuery],
manager: WindowsManager,
context: Context
):
if isinstance(update, CallbackQuery):
return True # skip middleware processing, and render window
try:
age = int(update.text) # try to convert text to age int
except ValueError:
return False # skip window rendering
# context.window_name = "error_window" # or switch window
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.