A Lightweight BOT Framework.
Project description
FastBot
A lightweight bot framework base on FastAPI and OneBot v11 protocol.
Quick Start
Installation
Install from Github
pip install --no-cache --upgrade git+https://github.com/OrganRemoved/fastbot.git
or
pip install --no-cache --upgrade https://github.com/OrganRemoved/fastbot/archive/refs/heads/main.zip
Install from PYPI
pip install --no-cache --upgrade fastbot-onebot
Example
The directory structure is as follows:
bot_example
| __init__.py
| bot.py
|
\---plugins
__init__.py
plugin_example.py
bot.py
from contextlib import asynccontextmanager
from typing import AsyncGenerator
from fastapi import FastAPI
from fastbot.bot import FastBot
from fastbot.plugin import PluginManager
@asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
# Register a websocket adapter to `FastAPI`
app.add_api_websocket_route("/onebot/v11/ws", FastBot.ws_adapter)
await asyncio.gather(
*(
init() if asyncio.iscoroutinefunction(init) else asyncio.to_thread(init)
for plugin in PluginManager.plugins.values()
if (init := plugin.init)
),
)
yield
if __name__ == "__main__":
(
FastBot
# `plugins` parameter will pass to `fastbot.plugin.PluginManager.import_from(...)`
# the rest parameter will pass to `FastAPI(...)`
.build(plugins=["plugins"], lifespan=lifespan)
# Parameter will pass to `uvicorn.run(...)`
.run(host="0.0.0.0", port=80)
)
plugin_example.py
from typing import AsyncGenerator
from fastbot.event import Context
from fastbot.event.message import GroupMessageEvent, PrivateMessageEvent
from fastbot.matcher import Matcher
from fastbot.plugin import Dependency, PluginManager, middleware, on
from redis.asyncio.client import Redis, Pipeline
# Passing rules to the matcher
IsNotGroupAdmin = Matcher(rule=lambda event: event.sender.role != "admin")
# Refactoring the Matcher
class IsInGroupBlacklist(Matcher):
def __init__(self, *blacklist):
self.blacklist = blacklist
def __call__(self, event: GroupMessageEvent) -> bool:
return event.group_id in self.blacklist
async def init() -> None:
# Do some initial work here
...
# Dependency injection
async def get_redis(*args, **kwargs) -> AsyncGenerator[Redis, None]:
if "url" in kwargs:
redis = Redis.from_url(decode_responses=True, *args, **kwargs)
elif "connection_pool" in kwargs:
redis = Redis.from_pool(*args, **kwargs)
else:
redis = Redis(
host=kwargs.pop("host", environ["REDIS_HOST"]),
port=kwargs.pop("port", int(environ["REDIS_PORT"])),
db=kwargs.pop("db", environ["REDIS_DB"]),
password=kwargs.pop("password", environ["REDIS_PASSWORD"]),
decode_responses=kwargs.pop("decode_responses", True),
**kwargs,
)
async with redis as r:
yield r
# Chaining dependency injection
async def get_pipeline(
redis: Redis = Dependency.provide(dependency=redis), *args, **kwargs
) -> AsyncGenerator[Pipeline, None]:
async with redis.pipeline(*args, **kwargs) as pipeline:
yield pipeline
await pipeline.execute()
# All middlewares will be executed in sequence
@middleware(priority=0)
async def preprocessing(ctx: Context):
if (group_id := ctx.get("group_id")) == ...:
# Temporarily disable the plugin
PluginManager.plugins["plugins.plugin_example"].state.set(False)
elif group_id is None:
# When the `Context` is clear, the middleware will discard
# the event and terminate processing
ctx.clear()
# Combining multiple rules via `&(and)`, `|(or)`,`~(not)`
@on(matcher=IsNotGroupAdmin & ~IsInGroupBlacklist(...))
# For the best performance, you can use `callable function`
# E.g. `lambda event: event.get("group_id") in (...)`
async def func(
# The event type to be handled must be specified via type hints
# You can use `|` or `typing.Union` types
event: GroupMessageEvent | PrivateMessageEvent,
*,
redis: Redis = Dependency.provide(dependency=get_redis),
pipeline: Pipeline = Dependency.provide(dependency=get_pipeline),
) -> None:
if event.text == "guess":
await event.send("Start guessing the number game now: [0-10]!")
while new_event := await event.defer("Enter a number: "):
if new_event.text != "10":
await new_event.send("Guess wrong!")
continue
await new_event.send("Guess right!")
return
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.
Source Distribution
fastbot_onebot-2025.2.28.tar.gz
(11.4 kB
view details)
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fastbot_onebot-2025.2.28.tar.gz.
File metadata
- Download URL: fastbot_onebot-2025.2.28.tar.gz
- Upload date:
- Size: 11.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.9.21
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
298f3085af5b81bb40ac116fb62f20667ef08f42dc008cd4704343362a03d368
|
|
| MD5 |
b03dfa3e8e42d1cf4202e0329d5e1529
|
|
| BLAKE2b-256 |
addd5f50053d2986bfabd50e709e295b02b26fddfbae190d9c3d8882a19d0c39
|
File details
Details for the file fastbot_onebot-2025.2.28-py3-none-any.whl.
File metadata
- Download URL: fastbot_onebot-2025.2.28-py3-none-any.whl
- Upload date:
- Size: 15.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.9.21
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bb59e87d22af09ca26d462909f2a72a8e1b9b5f8e83026644c304ba2ab258199
|
|
| MD5 |
0575c40f502676b3674eb05e8e01061c
|
|
| BLAKE2b-256 |
d4291451612f44b3095f97b0965eb8be34609262cbc7e1b8a661bfa442abf22c
|