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.3.1.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.3.1.tar.gz.
File metadata
- Download URL: fastbot_onebot-2025.3.1.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 |
369ffa89828b27dc66fff4b49ac29f051de8f59c25084a4b517d1930e0155fe2
|
|
| MD5 |
2943c15337973dfc48fcc52cab898a4e
|
|
| BLAKE2b-256 |
88dcd0196028b165df9c1bd014f8f31e03a9f690bbf8cb8a4460beeda4a9bc5a
|
File details
Details for the file fastbot_onebot-2025.3.1-py3-none-any.whl.
File metadata
- Download URL: fastbot_onebot-2025.3.1-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 |
fe1a5caad01d3a3a032ab141ca9c3736e57d82ccfa81155838d522b9cacf6d05
|
|
| MD5 |
2973d3ebd95dcd10bf1effec3df9bd88
|
|
| BLAKE2b-256 |
3a584dbdcc5f6ec5dc4e7b6414be34e55a7772735f7c916cd8690b5a693c9897
|