Bots development¶
Basic set up¶
For basic set up you should consider to use a HTTP-server with two reserved endpoints:
/status(incoming GET-requests)/command(incoming POST-requests)
You need to choose the server depending on which type of Bot you want to use: Sync or Async. It is supposed to use the Sync Bot with built-in servers, for instance, in Flask, Django, etc. and Async Bot with aiohttp, sanic, etc.
To initiate different types of Bots:
# Sync Bot
from botx import Bot
bot = Bot() # Sync Bot by default
# Async Bot
from botx import AsyncBot
bot = AsyncBot()
The bots have the same methods, the only difference is in synchronous/asynchronous execution and, as a result, in their usage.
Starting and stopping¶
For handling commands (explained in Handling commands) you need to start
your bot. You should do it with start() method:
start()method of the Sync Botbotx.bot.syncbot.SyncBot.start()start()method of the Async Botbotx.bot.asyncbot.AsyncBot.start()
If you need to shutdown your bot, you can do it with stop() method:
stop()method of the Sync Botbotx.bot.syncbot.SyncBot.stop()stop()method of the Async Botbotx.bot.asyncbot.AsyncBot.stop()
Synchronous Bot
from botx import Bot
bot = Bot()
bot.start()
Asynchronous Bot
Example with FastAPI:
from botx import AsyncBot
from fastapi import FastAPI
bot = AsyncBot()
app = FastAPI()
@app.on_event('startup')
async def on_startup():
await bot.start()
@app.on_event('shutdown')
async def on_shutdown():
await bot.stop()
Receiving statuses and commands¶
The BotX platform sends requests of two types. First are status requests, which are used for getting the basic information about the Bot (like it’s status and commands that bot can understand). Second are command requests, which are used for passing the commands from users to the Bot. In both cases you need to parse these incoming requests. Python Lib for BotX has built-in parsers for that:
parse_status()method of the Sync Botbotx.bot.syncbot.SyncBot.parse_status()parse_command()method of the Sync Botbotx.bot.syncbot.SyncBot.parse_command()parse_status()method of the Async Botbotx.bot.asyncbot.AsyncBot.parse_status()parse_command()method of the Async Botbotx.bot.asyncbot.AsyncBot.parse_command()
Note
Note that in success cases the /status endpoint must return the
HTTP 200 OK success status with response generated by Bot and the
/command endpoint must return the HTTP 202 ACCEPTED
Synchronous Bot
Example in Flask:
from botx import Bot
from flask import Flask, request
app = Flask(__name__)
bot = Bot()
@app.route('/status', methods=['GET'])
def status_endpoint():
status = bot.parse_status()
return app.response_class(
response=status.json().encode('utf-8'),
status=200,
mimetype='application/json'
)
@app.route('/command', methods=['POST'])
def command_endpoint():
bot.parse_command(request.data)
return app.response_class(
response=json.dumps({'status': 'accepted'}).encode('utf-8'),
status=202,
mimetype='application/json'
)
Asynchronous Bot
Example in FastAPI:
from botx import AsyncBot, Status
from fastapi import FastAPI
from starlette.exceptions import HTTPException
bot = AsyncBot()
app = FastAPI()
@app.get('/status', response_model=Status, response_code=202)
async def status():
return await bot.parse_status()
@app.post('/command')
async def command(data: Dict[str, Any]):
if await bot.parse_command(data):
return {'result': 'accepted'}
raise HTTPException(
detail={'result': 'not found'},
status_code=404,
)
Handling commands¶
Python Lib for BotX also supports automatic command handling. For that you need to link your command with your callable method. You can achieve this by using methods and decorators listed below.
When you link your method with your command, you should add message as an
incoming parameter to your function.
Note
message parameter is of Message type
botx.types.message.Message
Adding link between the method and incoming command /hello. First way,
using add_handler method botx.bot.basebot.BaseBot.add_handler() and
built-in class botx.bot.dispatcher.commandhandler.CommandHandler:
Synchronous Bot
from botx import Bot, CommandHandler
bot = Bot()
def say_hello(message):
bot.send_message(
'Hello! Your message was {}'.format(message.body),
message.sync_id,
message.bot_id,
message.host
)
bot.add_handler(
CommandHandler(
name='Hello',
command='/hello',
description='Hello command',
func=say_hello
)
)
Asynchronous Bot
from botx import AsyncBot, CommandHandler
bot = AsyncBot()
async def say_hello(message, bot):
await bot.send_message(
'Hello! Your message was {}'.format(message.body),
message.sync_id,
message.bot_id,
message.host
)
await bot.add_handler(
CommandHandler(
name='Hello',
command='/hello',
description='Hello command',
func=say_hello
)
)
Second way of adding link between the method and incoming command /hello,
using built-in command decorator
botx.bot.router.CommandRouter.command():
Synchronous Bot
from botx import Bot
bot = Bot()
@bot.command(name='Hello', body='/hello', description='Hello command')
def say_hello(message):
bot.send_message(
'Hello! Your message was {}'.format(message.body),
message.sync_id,
message.bot_id,
message.host
)
Asynchronous Bot
from botx import AsyncBot
bot = AsyncBot()
@bot.command(name='Hello', body='/hello', description='Hello command')
async def say_hello(message, bot):
await bot.send_message(
'Hello! Your message was {}'.format(message.body),
message.sync_id,
message.bot_id,
message.host
)
Note
For handling of ANY command, you may use use_as_default_handler=True
parameter of CommandHandler
bot.add_handler(
CommandHandler(
# necessary parameters: name, command, description, func
# ...
use_as_default_handler=True
)
)
# or
@bot.command(use_as_default_handler=True)
def some_func(message):
pass
Sending messages¶
To send a message you need simply use the send_message method of the Bot:
botx.bot.syncbot.SyncBot.send_message()botx.bot.asyncbot.AsyncBot.send_message()
Note
However, there is a tiny issue. In some cases you should send messages via
sync_id and in some cases you should send messages via group_chat_id
parameter of Message object botx.types.message.Message. If you
have the incoming message object, then send_message via
message.sync_id, if you do not have any messages, then you should know
the group_chat_id, bot_id, host and send messages with them
(probably, you will need a database, where you will save all data from
messages).
Synchronous Bot
# When you have a message, then send by it's sync_id
@bot.command
def some_command(message):
bot.send_message(
'Text',
message.sync_id,
message.bot_id,
message.host
)
# When you do not have a message
def some_func():
# Query your group_chat_id from, for instance, your database
group_chat_id = 'group_chat_id that you have saved to somewhere'
bot_id = 'bot_id that you have saved to somewhere'
host = 'host that you have saved to somewhere'
bot.send_message(
'Text',
group_chat_id,
bot_id,
host
)
Asynchronous Bot
# When you have a message, then send by it's sync_id
@bot.command
async def some_command(message):
await bot.send_message(
'Text',
message.sync_id,
message.bot_id,
message.host
)
# When you do not have a message
async def some_func():
# Query your group_chat_id from, for instance, your database
group_chat_id = 'group_chat_id that you have saved to somewhere'
bot_id = 'bot_id that you have saved to somewhere'
host = 'host that you have saved to somewhere'
await bot.send_message(
'Text',
group_chat_id,
bot_id,
host
)
Sending files¶
To send a file you need to use the send_file method of the Bot:
botx.bot.syncbot.SyncBot.send_file()botx.bot.asyncbot.AsyncBot.send_file()
Bubbles, Keyboards, etc.¶
You can add your custom Bubbles (buttons under the message) and Keyboards. For these purposes there are two objects:
BubbleElementobjectbotx.types.bubble.BubbleElementKeyboardElementobjectbotx.types.keyboard.KeyboardElement
You can add these elements to nested lists ( list(list()) –> [[]] )
with the message. For example:
@bot.command
def some_command(message):
bot.send_message(
'Text',
message.sync_id,
message.bot_id,
message.host,
bubble=[
[
BubbleElement(label='yes', command='/yes'),
BubbleElement(label='no', command='/no')
],
[
BubbleElement(label='something', command='/something')
]
],
keyboard=[
[
KeyboardElement(label='yes', command='/yes'),
KeyboardElement(label='no', command='/no')
],
[
KeyboardElement(label='something', command='/something')
]
]
)