Compare commits

...

33 Commits

Author SHA1 Message Date
ad70648ea2 Update dependency mypy to v1.15.0
Some checks failed
Analysis / SonarCloud (pull_request) Failing after 4s
Tests / Build and Test (3.11) (pull_request) Successful in 1m6s
Tests / Build and Test (3.12) (pull_request) Successful in 1m4s
Tests / Build and Test (3.13) (pull_request) Successful in 1m0s
Analysis / SonarCloud (push) Failing after 4s
2025-02-05 06:31:00 +02:00
09b4d512a6 Merge pull request 'Update dependency pytest-asyncio to v0.25.3' (#184) from renovate/pytest-asyncio-0.x into dev
Some checks failed
Analysis / SonarCloud (push) Failing after 3s
Reviewed-on: #184
2025-01-29 10:48:23 +02:00
1473d34ca1 Merge pull request 'Update dependency black to v25' (#185) from renovate/black-25.x into dev
Some checks failed
Analysis / SonarCloud (push) Failing after 4s
Reviewed-on: #185
2025-01-29 10:43:45 +02:00
5fc8ae6a6e Update dependency black to v25
Some checks failed
Analysis / SonarCloud (pull_request) Failing after 4s
Tests / Build and Test (3.11) (pull_request) Successful in 1m9s
Tests / Build and Test (3.12) (pull_request) Successful in 1m5s
Tests / Build and Test (3.13) (pull_request) Successful in 1m4s
2025-01-29 06:29:48 +02:00
8562d7e84c Update dependency pytest-asyncio to v0.25.3
Some checks failed
Analysis / SonarCloud (pull_request) Failing after 4s
Tests / Build and Test (3.11) (pull_request) Successful in 1m19s
Tests / Build and Test (3.12) (pull_request) Successful in 1m20s
Tests / Build and Test (3.13) (pull_request) Successful in 1m18s
2025-01-28 21:04:58 +02:00
258b46d829 Update dependency pylint to v3.3.4
Some checks failed
Analysis / SonarCloud (pull_request) Failing after 4s
Tests / Build and Test (3.11) (pull_request) Successful in 1m12s
Tests / Build and Test (3.12) (pull_request) Successful in 1m7s
Tests / Build and Test (3.13) (pull_request) Successful in 1m6s
Analysis / SonarCloud (push) Failing after 4s
2025-01-28 15:50:37 +02:00
efec002667 Merge pull request 'Update dependency twine to v6.1.0' (#181) from renovate/twine-6.x into dev
Some checks failed
Analysis / SonarCloud (push) Failing after 3s
Reviewed-on: #181
2025-01-24 16:38:45 +02:00
475eaf9ff3 Update dependency twine to v6.1.0
Some checks failed
Analysis / SonarCloud (pull_request) Failing after 3s
Tests / Build and Test (3.11) (pull_request) Successful in 1m31s
Tests / Build and Test (3.12) (pull_request) Successful in 1m23s
Tests / Build and Test (3.13) (pull_request) Successful in 1m14s
2025-01-21 21:37:09 +02:00
0fcd9f2041 Merge pull request 'Update dependency tox to v4.24.0' (#179) from renovate/tox-4.x into dev
Some checks failed
Analysis / SonarCloud (push) Failing after 4s
Reviewed-on: #179
2025-01-21 21:12:20 +02:00
44d07dc56a Update dependency tox to v4.24.0
Some checks failed
Tests / Build and Test (3.11) (pull_request) Successful in 1m17s
Tests / Build and Test (3.13) (pull_request) Successful in 1m6s
Tests / Build and Test (3.12) (pull_request) Successful in 1m9s
Analysis / SonarCloud (pull_request) Failing after 4s
2025-01-21 20:32:47 +02:00
c5e83c17d3 Disabled pip cache for publish because dependencies are inline
All checks were successful
Analysis / SonarCloud (push) Successful in 42s
2025-01-10 11:23:35 +02:00
129cbd923b Updated cache path for tests
All checks were successful
Analysis / SonarCloud (push) Successful in 41s
2025-01-10 11:22:33 +02:00
1ca126829b Hopefully fixed caching
All checks were successful
Analysis / SonarCloud (push) Successful in 49s
2025-01-10 00:09:05 +01:00
974aebfd1a Bruh, works exactly as bad. I give up... Let's cache this shit.
Some checks failed
Analysis / SonarCloud (push) Has been cancelled
2025-01-09 23:08:09 +01:00
ed7fa50dbd SonarQube works like shit, switching back to the old SonarCloud action
All checks were successful
Analysis / SonarCloud (push) Successful in 9m49s
2025-01-09 22:54:21 +01:00
82542de0bb SonarQube doesn't seem to work, one more try with latest
Some checks failed
Analysis / SonarCloud (push) Has been cancelled
2025-01-09 22:40:32 +01:00
9021eac87b SonarCloud action is deprecated, replacing with sonarqube one and returning to ubuntu-24.04
Some checks failed
Analysis / SonarCloud (push) Has been cancelled
2025-01-09 22:35:53 +01:00
651022ab6e SonarCloud doesn't like 22.04 either, trying ubuntu-latest instead
Some checks failed
Analysis / SonarCloud (push) Has been cancelled
2025-01-09 22:33:38 +01:00
f8c6b782a1 SonarCloud doesn't like 24.04, trying 22.04 instead
Some checks failed
Analysis / SonarCloud (push) Has been cancelled
2025-01-09 22:32:24 +01:00
a1d0b98858 Replaced ubuntu-latest with ubuntu-24.04
Some checks failed
Analysis / SonarCloud (push) Has been cancelled
2025-01-09 22:18:57 +01:00
fec40b1c44 Update dependency pytest-asyncio to v0.25.2
All checks were successful
Analysis / SonarCloud (pull_request) Successful in 34s
Tests / Build and Test (3.11) (pull_request) Successful in 1m15s
Tests / Build and Test (3.12) (pull_request) Successful in 1m21s
Tests / Build and Test (3.13) (pull_request) Successful in 1m20s
Analysis / SonarCloud (push) Successful in 46s
2025-01-08 08:41:33 +02:00
kku
e9b9fc6ca1 TEST: Publishing Action
All checks were successful
Analysis / SonarCloud (push) Successful in 46s
Analysis / SonarCloud (pull_request) Successful in 41s
Tests / Build and Test (3.11) (pull_request) Successful in 1m19s
Tests / Build and Test (3.12) (pull_request) Successful in 1m24s
Tests / Build and Test (3.13) (pull_request) Successful in 1m24s
2025-01-02 13:58:23 +01:00
1da367ccb1 Update dependency pytest-asyncio to v0.25.1
All checks were successful
Analysis / SonarCloud (push) Successful in 41s
Analysis / SonarCloud (pull_request) Successful in 40s
Tests / Build and Test (3.11) (pull_request) Successful in 1m15s
Tests / Build and Test (3.12) (pull_request) Successful in 1m22s
Tests / Build and Test (3.13) (pull_request) Successful in 1m22s
2025-01-02 08:03:07 +02:00
kku
d5e390fe66 Optimized json_load usage, imports and typing
All checks were successful
Analysis / SonarCloud (push) Successful in 48s
Analysis / SonarCloud (pull_request) Successful in 39s
Tests / Build and Test (3.11) (pull_request) Successful in 1m22s
Tests / Build and Test (3.12) (pull_request) Successful in 1m27s
Tests / Build and Test (3.13) (pull_request) Successful in 1m29s
2025-01-01 22:34:38 +01:00
kku
ae54bd5cce Bump version to 4.0.2
All checks were successful
Analysis / SonarCloud (push) Successful in 42s
Analysis / SonarCloud (pull_request) Successful in 39s
Tests / Build and Test (3.11) (pull_request) Successful in 1m19s
Tests / Build and Test (3.12) (pull_request) Successful in 1m22s
Tests / Build and Test (3.13) (pull_request) Successful in 1m23s
2024-12-31 11:16:16 +01:00
kku
9ce251d733 Added a quick README for examples (belongs to #60)
All checks were successful
Analysis / SonarCloud (push) Successful in 41s
2024-12-31 11:10:06 +01:00
kku
5dd873d683 Closes #61
All checks were successful
Analysis / SonarCloud (push) Successful in 1m1s
2024-12-31 11:07:24 +01:00
b47bcbe513 Update dependency mypy to v1.14.1
All checks were successful
Analysis / SonarCloud (pull_request) Successful in 35s
Tests / Build and Test (3.11) (pull_request) Successful in 1m14s
Tests / Build and Test (3.12) (pull_request) Successful in 1m37s
Tests / Build and Test (3.13) (pull_request) Successful in 1m22s
Analysis / SonarCloud (push) Successful in 47s
2024-12-30 19:17:40 +02:00
kku
bbbec75f91 Fixed naming conventions
All checks were successful
Analysis / SonarCloud (push) Successful in 44s
2024-12-29 19:27:42 +01:00
kku
94553b602e Fixed imports in examples 2024-12-29 16:27:58 +01:00
kku
3cdd6da506 Added typing_extensions to the dependencies
All checks were successful
Analysis / SonarCloud (push) Successful in 58s
Analysis / SonarCloud (pull_request) Successful in 37s
Tests / Build and Test (3.11) (pull_request) Successful in 1m21s
Tests / Build and Test (3.12) (pull_request) Successful in 1m27s
Tests / Build and Test (3.13) (pull_request) Successful in 1m49s
2024-12-29 16:06:45 +01:00
kku
d24e94b57e Tests are now for 3.11+
All checks were successful
Analysis / SonarCloud (push) Successful in 44s
2024-12-27 18:33:51 +01:00
95584c0e63 Slight documentation improvements
All checks were successful
Analysis / SonarCloud (push) Successful in 46s
2024-12-27 00:37:54 +01:00
14 changed files with 192 additions and 83 deletions

View File

@@ -6,19 +6,19 @@ on:
- main
- dev
pull_request:
types: [opened, synchronize, reopened]
types: [ opened, synchronize, reopened ]
jobs:
sonarcloud:
name: SonarCloud
runs-on: ubuntu-latest
container: catthehacker/ubuntu:act-latest
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: SonarCloud Scan
uses: SonarSource/sonarcloud-github-action@master
- uses: SonarActions/cache@v1
- name: SonarQube Scan
uses: SonarSource/sonarqube-scan-action@v4.2.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}

View File

@@ -0,0 +1,67 @@
name: Upload Python Package
on:
release:
types: [ published ]
permissions:
contents: read
jobs:
release-build:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.x"
- name: Build release distributions
run: |
python -m pip install build
python -m build
- name: Upload distributions
uses: christopherhx/gitea-upload-artifact@v4
with:
name: release-dists
path: dist/
gitea-publish:
runs-on: ubuntu-24.04
needs: release-build
permissions:
id-token: write
environment:
name: gitea
url: https://git.end-play.xyz/profitroll/-/packages/pypi/libbot
env:
GITHUB_WORKFLOW_REF: ${{ gitea.workflow_ref }}
INPUT_REPOSITORY_URL: https://git.end-play.xyz/api/packages/profitroll/pypi
steps:
- name: Retrieve release distributions
uses: christopherhx/gitea-download-artifact@v4
with:
name: release-dists
path: dist/
- name: Publish package distributions to TestPyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.PYPI_GITEA_API_TOKEN }}
repository-url: https://git.end-play.xyz/api/packages/profitroll/pypi
pypi-publish:
runs-on: ubuntu-24.04
needs: release-build
permissions:
id-token: write
environment:
name: pypi
env:
GITHUB_WORKFLOW_REF: ${{ gitea.workflow_ref }}
steps:
- name: Retrieve release distributions
uses: christopherhx/gitea-download-artifact@v4
with:
name: release-dists
path: dist/
- name: Publish package distributions to TestPyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.PYPI_PYPI_API_TOKEN }}

View File

@@ -11,18 +11,18 @@ on:
jobs:
test:
name: Build and Test
runs-on: ubuntu-latest
container: catthehacker/ubuntu:act-latest
runs-on: ubuntu-24.04
strategy:
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12"]
python-version: [ "3.11", "3.12", "3.13" ]
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
cache-dependency-path: './requirements/*'
env:
AGENT_TOOLSDIRECTORY: /opt/hostedtoolcache
- name: Install dependencies

View File

@@ -36,19 +36,20 @@ pip install libbot[pycord,speed]
### Pyrogram
```python
from libbot.pyrogram import PyroClient
import sys
from libbot.pyrogram.classes import PyroClient
def main():
client = PyroClient(scheduler=scheduler)
client: PyroClient = PyroClient()
try:
client.run()
except KeyboardInterrupt:
print("Shutting down...")
finally:
if client.scheduler is not None:
client.scheduler.shutdown()
exit()
sys.exit()
if __name__ == "__main__":
@@ -58,29 +59,33 @@ if __name__ == "__main__":
### Pycord
```python
import asyncio
from asyncio import AbstractEventLoop
from discord import Intents
from libbot import sync
from libbot.pycord import PycordBot
from libbot.utils import config_get
from libbot.pycord.classes import PycordBot
async def main():
intents = Intents.default()
bot = PycordBot(intents=intents)
intents: Intents = Intents.default()
bot: PycordBot = PycordBot(intents=intents)
bot.load_extension("cogs")
try:
await bot.start(sync.config_get("bot_token", "bot"))
await bot.start(config_get("bot_token", "bot"))
except KeyboardInterrupt:
logger.warning("Shutting down...")
print("Shutting down...")
await bot.close()
if __name__ == "__main__":
loop = asyncio.get_event_loop()
loop: AbstractEventLoop = asyncio.get_event_loop()
loop.run_until_complete(main())
```
## Config examples
For bot config examples please check the examples directory. Without a valid config file, the bot won't start at all, so you need to make sure the correct config file is used.
For bot config examples please check the examples directory. Without a valid config file, the bot won't start at all, so
you need to make sure the correct config file is used.

4
examples/README.md Normal file
View File

@@ -0,0 +1,4 @@
# Examples
If you're looking for Pyrogram usage examples, please take a look at
the [PyrogramBotBase](https://git.end-play.xyz/profitroll/PyrogramBotBase) repository.

View File

@@ -1 +1,2 @@
aiofiles>=23.0.0
aiofiles>=23.0.0
typing-extensions~=4.12.2

View File

@@ -1,11 +1,12 @@
black==24.10.0
black==25.1.0
build==1.2.2.post1
isort==5.13.2
mypy==1.14.0
pylint==3.3.3
pytest-asyncio==0.25.0
mypy==1.15.0
pylint==3.3.4
pytest-asyncio==0.25.3
pytest-cov==6.0.0
pytest==8.3.4
tox==4.23.2
tox==4.24.0
twine==6.1.0
types-aiofiles==24.1.0.20241221
types-ujson==5.10.0.20240515

View File

@@ -1,4 +1,4 @@
__version__ = "4.0.0"
__version__ = "4.0.2"
__license__ = "GPL3"
__author__ = "Profitroll"

View File

@@ -0,0 +1,2 @@
# This file is left empty on purpose
# Adding imports here will cause import errors when libbot[pycord] is not installed

View File

@@ -1,9 +1,13 @@
import logging
from logging import Logger
from pathlib import Path
from typing import Any, Dict
from typing_extensions import override
from ...i18n.classes import BotLocale
from ...utils import json_read
try:
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from apscheduler.schedulers.background import BackgroundScheduler
@@ -11,32 +15,21 @@ try:
except ImportError as exc:
raise ImportError("You need to install libbot[pycord] in order to use this class.") from exc
try:
from ujson import loads
except ImportError:
from json import loads
from ...i18n.classes import BotLocale
logger = logging.getLogger(__name__)
logger: Logger = logging.getLogger(__name__)
class PycordBot(Bot):
@override
def __init__(
self,
*args,
config: Dict[str, Any] | None = None,
config_path: str | Path = Path("config.json"),
locales_root: str | Path | None = None,
scheduler: AsyncIOScheduler | BackgroundScheduler | None = None,
**kwargs,
self,
*args,
config: Dict[str, Any] | None = None,
config_path: str | Path = Path("config.json"),
locales_root: str | Path | None = None,
scheduler: AsyncIOScheduler | BackgroundScheduler | None = None,
**kwargs,
):
if config is None:
with open(config_path, "r", encoding="utf-8") as f:
self.config: dict = loads(f.read())
else:
self.config = config
self.config: Dict[str, Any] = config if config is not None else json_read(config_path)
super().__init__(
debug_guilds=(self.config["bot"]["debug_guilds"] if self.config["debug"] else None),

View File

@@ -0,0 +1 @@
from .color import color_from_hex, hex_from_color

View File

@@ -0,0 +1,35 @@
from discord import Colour
def _int_from_hex(hex_string: str) -> int:
try:
return int(hex_string, base=16)
except Exception as exc:
raise ValueError("Input string must be a valid HEX code.") from exc
def _hex_from_int(color_int: int) -> str:
if not 0 <= color_int <= 0xFFFFFF:
raise ValueError("Color's value must be in the range 0 to 0xFFFFFF.")
return f"#{color_int:06x}"
def color_from_hex(hex_string: str) -> Colour:
"""Convert valid hexadecimal string to discord.Colour.
:param hex_string: Hexadecimal string to convert into Colour object
:type hex_string: str
:return: Colour object
"""
return Colour(_int_from_hex(hex_string))
def hex_from_color(color: Colour) -> str:
"""Convert discord.Colour to hexadecimal string.
:param color: Colour object to convert into the string
:type color: Colour
:return: Hexadecimal string in #XXXXXX format
"""
return _hex_from_int(color.value)

View File

@@ -0,0 +1,2 @@
# This file is left empty on purpose
# Adding imports here will cause import errors when libbot[pyrogram] is not installed

View File

@@ -2,6 +2,7 @@ import asyncio
import logging
import sys
from datetime import datetime, timedelta
from logging import Logger
from os import cpu_count, getpid
from pathlib import Path
from time import time
@@ -9,6 +10,12 @@ from typing import Any, Dict, List
from typing_extensions import override
from .command import PyroCommand
from .commandset import CommandSet
from ...i18n import _
from ...i18n.classes import BotLocale
from ...utils import json_read
try:
import pyrogram
from apscheduler.schedulers.asyncio import AsyncIOScheduler
@@ -35,42 +42,33 @@ try:
except ImportError:
from json import dumps, loads
from ...i18n.classes import BotLocale
from ...i18n import _
from .command import PyroCommand
from .commandset import CommandSet
logger = logging.getLogger(__name__)
logger: Logger = logging.getLogger(__name__)
class PyroClient(Client):
@override
def __init__(
self,
name: str = "bot_client",
owner: int | None = None,
config: Dict[str, Any] | None = None,
config_path: str | Path = Path("config.json"),
api_id: int | None = None,
api_hash: str | None = None,
bot_token: str | None = None,
workers: int = min(32, cpu_count() + 4),
locales_root: str | Path | None = None,
plugins_root: str = "plugins",
plugins_exclude: List[str] | None = None,
sleep_threshold: int = 120,
max_concurrent_transmissions: int = 1,
commands_source: Dict[str, dict] | None = None,
scoped_commands: bool | None = None,
i18n_bot_info: bool = False,
scheduler: AsyncIOScheduler | BackgroundScheduler | None = None,
**kwargs,
self,
name: str = "bot_client",
owner: int | None = None,
config: Dict[str, Any] | None = None,
config_path: str | Path = Path("config.json"),
api_id: int | None = None,
api_hash: str | None = None,
bot_token: str | None = None,
workers: int = min(32, cpu_count() + 4),
locales_root: str | Path | None = None,
plugins_root: str = "plugins",
plugins_exclude: List[str] | None = None,
sleep_threshold: int = 120,
max_concurrent_transmissions: int = 1,
commands_source: Dict[str, dict] | None = None,
scoped_commands: bool | None = None,
i18n_bot_info: bool = False,
scheduler: AsyncIOScheduler | BackgroundScheduler | None = None,
**kwargs,
):
if config is None:
with open(config_path, "r", encoding="utf-8") as f:
self.config: dict = loads(f.read())
else:
self.config = config
self.config: Dict[str, Any] = config if config is not None else json_read(config_path)
super().__init__(
name=name,
@@ -211,7 +209,7 @@ class PyroClient(Client):
@override
async def stop(
self, exit_completely: bool = True, scheduler_shutdown: bool = True, scheduler_wait: bool = True
self, exit_completely: bool = True, scheduler_shutdown: bool = True, scheduler_wait: bool = True
) -> None:
try:
await self.send_message(
@@ -310,7 +308,7 @@ class PyroClient(Client):
# in it, if there are any. Then adds them to self.commands
for handler in self.dispatcher.groups[0]:
if isinstance(handler, MessageHandler) and (
hasattr(handler.filters, "base") or hasattr(handler.filters, "other")
hasattr(handler.filters, "base") or hasattr(handler.filters, "other")
):
for entry in [handler.filters.base, handler.filters.other]:
if hasattr(entry, "commands"):
@@ -321,8 +319,8 @@ class PyroClient(Client):
return command_sets
def add_command(
self,
command: str,
self,
command: str,
) -> None:
"""Add command to the bot's internal commands list