Skip to main content

A modern async Python library for interacting with Soroush Plus

Project description

SplusPy

A modern async Python library for Soroush Plus

License: MIT PyPI version Python versions Downloads Code style: black Type checked: mypy

English | فارسی


SplusPy is a modern, asynchronous Python library for interacting with Soroush Plus — both as a user account and a bot account.

Built from the ground up with clean architecture, it's designed to feel like Telethon or Pyrogram, but specifically for the Soroush Plus ecosystem.

SplusPy یک کتابخانه پایتون مدرن و ناهمگام (Asynchronous) برای تعامل با سروش پلاس است — هم به عنوان حساب کاربری و هم به عنوان حساب ربات.

این کتابخانه از صفر با معماری تمیز طراحی شده و الهام گرفته از Telethon و Pyrogram است، اما مخصوص اکوسیستم سروش پلاس می‌باشد.


English

Features

Feature Description
No API Key Required Built-in Soroush Plus credentials
Fully Asynchronous Built with Python's asyncio
Sync Support Use without async/await via spluspy.sync
Bot & User Support Both account types
Event-Driven Handlers Powerful event system with decorators
Filter System Composable filters (&, |, ~)
Inline & Reply Buttons Interactive keyboards
Conversation API For interactive bot flows
FSM (Finite State Machine) Built-in state management for bots
Plugin System Dynamic plugin loading
Middleware Pre/post processing of updates
Scheduler Built-in task scheduler
Multiple Storage Backends Memory, SQLite, Redis, PostgreSQL
Rate Limiting Token bucket algorithm with flood wait handling
AFK Auto-Reply Smart auto-responder with per-chat rate limiting
Chat Administration Ban, mute, pin, purge — high-level admin API
Message Mirroring Real-time message cloning between chats
Batch Operations Send, delete, forward multiple messages at once
File Transfer with Progress Upload/download with progress tracking
Professional Logging Structured, namespaced loggers
Type Hints Everywhere Full type safety
Clean Architecture SOLID principles, modular design

Requirements

  • Python 3.10+
  • No external API keys needed (built-in Soroush Plus credentials)

Installation

pip install spluspy

For faster encryption:

pip install spluspy[speed]

With Redis backend:

pip install spluspy[redis]

With PostgreSQL backend:

pip install spluspy[postgres]

All optional dependencies:

pip install spluspy[all]

Quick Start

Simplest Bot

from spluspy import Client

bot = Client("my_session")

@bot.on_message()
async def handler(client, message):
    await message.reply("Hello!")

bot.run()

User Account

from spluspy import Client

client = Client("session_name")

@client.on_message()
async def handler(client, message):
    await message.reply("Hey there!")

async def main():
    await client.start(phone="+98XXXXXXXXXX")
    await client.run_until_disconnected()

import asyncio
asyncio.run(main())

Sync Usage (No Async/Await)

from spluspy.sync import Client

bot = Client("session")

@bot.on_message()
def handler(client, message):
    message.reply("Hello!")

bot.run()

Events

Decorator Event
@bot.on_message() New message
@bot.on_edited() Message edited
@bot.on_callback_query() Inline button clicked
@bot.on_inline_query() Inline query
@bot.on_chat_action() Join/leave/pin
@bot.on_user_update() Status change
@bot.on_message_deleted() Message deleted
@bot.on_message_read() Read receipt
@bot.on_error() Global error handling

Event Priority

from spluspy.events import HandlerPriority

@bot.on_message(priority=HandlerPriority.FIRST)
async def high_priority_handler(client, message):
    # Runs first
    pass

@bot.on_message(priority=HandlerPriority.LOW)
async def low_priority_handler(client, message):
    # Runs last
    pass

Stop Propagation

@bot.on_message()
async def stopper(client, message):
    if message.text == "/stop":
        message.stop_propagation()
        await message.reply("Stopped!")

Filters

from spluspy import filters

@bot.on_message(filters.text)                    # Text only
@bot.on_message(filters.private)                 # Private chats
@bot.on_message(filters.group)                   # Groups
@bot.on_message(filters.command("start"))        # /start command
@bot.on_message(filters.regex(r"\d+"))           # Regex match
@bot.on_message(filters.user(123))               # Specific user
@bot.on_message(filters.text & filters.private)  # Combined
@bot.on_message(filters.photo | filters.video)   # Photo OR video

Message Methods

await message.reply("Hello")              # Reply
await message.edit("New text")            # Edit
await message.delete()                    # Delete
await message.forward(chat_id)            # Forward
await message.copy(chat_id)               # Copy (no forward header)
await message.pin()                       # Pin
await message.react("❤️")                 # React
await message.mark_read()                 # Mark as read
await message.download()                  # Download media
await message.reply_photo("photo.jpg")    # Reply with photo
await message.reply_video("video.mp4")    # Reply with video
await message.reply_document("file.pdf")  # Reply with document

Buttons

from spluspy import Button

# Inline keyboard
keyboard = Button.build_inline([
    Button.inline("Option 1", b"opt1"),
    Button.inline("Option 2", b"opt2")
])
await bot.send_message(chat_id, "Choose:", buttons=keyboard)

# Reply keyboard
kb = Button.build_reply([
    Button.text("Menu"),
    Button.text("Settings")
])
await bot.send_message(chat_id, "Pick:", buttons=kb)

# Clear keyboard
await bot.send_message(chat_id, "Done", buttons=Button.clear())

FSM (Finite State Machine)

from spluspy.fsm import State, StateMachine
from spluspy.storage import MemoryStorage

storage = MemoryStorage()
fsm = StateMachine(storage)

class Form:
    name = State()
    age = State()

@bot.on_message(filters.command("register"))
async def start_register(client, message):
    ctx = fsm.context(message.sender_id)
    await ctx.set_state(Form.name)
    await message.reply("What is your name?")

@bot.on_message(filters.private)
async def process_form(client, message):
    ctx = fsm.context(message.sender_id)
    state = await ctx.get_state()

    if state == Form.name:
        await ctx.set_data(name=message.text)
        await ctx.set_state(Form.age)
        await message.reply("How old are you?")
    elif state == Form.age:
        data = await ctx.get_data()
        await ctx.reset()
        await message.reply(f"Registered! Name: {data.get('name')}, Age: {message.text}")

Advanced FSM: State Routing with Decorators

from spluspy.fsm import State, StateMachine, StateTransition
from spluspy.storage import SQLiteStorage

storage = SQLiteStorage("fsm.db")
sm = StateMachine(storage)

class Registration:
    name = State()
    age = State()
    complete = State()

@sm.state(Registration.name)
async def handle_name(ctx, message):
    await ctx.set(name=message.text)
    return StateTransition(to=Registration.age)

@sm.state(Registration.age)
async def handle_age(ctx, message):
    await ctx.set(age=message.text)
    return StateTransition(to=Registration.complete)

@sm.state(Registration.complete)
async def handle_complete(ctx, message):
    data = await ctx.get()
    await message.reply(f"Done: {data}")
    await ctx.finish()

Storage Backends

from spluspy.storage import MemoryStorage, SQLiteStorage, RedisStorage, PostgresStorage, get_storage

# Memory (default)
storage = MemoryStorage()

# SQLite
storage = SQLiteStorage("data.db")

# Redis
storage = RedisStorage(host="localhost", port=6379, db=0)

# PostgreSQL
storage = PostgresStorage(dsn="postgresql://user:pass@localhost/db")

# Factory function
storage = get_storage("redis", host="localhost")

Plugin System

# plugins/hello.py
def register(client):
    @client.on_message(filters.command("hello"))
    async def hello_handler(client, message):
        await message.reply("Hello from plugin!")
# main.py
from spluspy import Client

bot = Client("session")
bot.plugins.load("plugins")
bot.run()

Middleware

from spluspy.middleware import Middleware

class LoggingMiddleware(Middleware):
    async def on_update(self, update, handler):
        print(f"Update received: {update}")
        result = await handler(update)
        print(f"Handler completed")
        return result

bot.middleware.add(LoggingMiddleware())

Rate Limiting Middleware

from spluspy.middleware import RateLimitMiddleware

# 10 messages per minute
rate_limit = RateLimitMiddleware(max_messages=10, window_seconds=60)
bot.middleware.add(rate_limit)

Rate Limiter

from spluspy.utils import RateLimiter

limiter = RateLimiter(max_calls=10, period=60)

@bot.on_message()
async def limited_handler(client, message):
    if not limiter.allow():
        await message.reply("Rate limited! Try again later.")
        return
    await message.reply("OK")

Cache

from spluspy.utils import LRUCache

cache = LRUCache(maxsize=1000, ttl=300)  # 5 min TTL
cache.set("key", "value")
value = cache.get("key")
stats = cache.stats()  # {'hits': 42, 'misses': 3}

AFK Auto-Reply

from spluspy import Client, filters
from spluspy.afk import AfkManager

bot = Client("my_account")
afk = AfkManager(bot, message="I'm currently AFK. Back soon!")

@bot.on_message(filters.command("afk"))
async def set_afk(client, message):
    afk.set_afk(True, reason="Lunch break")
    await message.reply("I'm now AFK!")

@bot.on_message(filters.command("back"))
async def unset_afk(client, message):
    afk.set_afk(False)
    await message.reply(f"Back! Sent {afk.total_replies} auto-replies.")

@bot.on_message(filters.private & filters.incoming)
async def auto_reply(client, message):
    if afk.is_afk:
        await afk.handle(message)

Chat Administration

from spluspy.admin import ChatAdmin

admin = ChatAdmin(bot)

await admin.ban_user(chat_id, user_id)
await admin.unban_user(chat_id, user_id)
await admin.mute_user(chat_id, user_id)
await admin.unmute_user(chat_id, user_id)
await admin.pin_message(chat_id, message)
await admin.unpin_message(chat_id, message)
await admin.unpin_all(chat_id)
await admin.purge_messages(chat_id, limit=100)

# Bulk actions
await admin.bulk_action(chat_id, "ban", [user_id1, user_id2, user_id3])

# Admin log
events = await admin.get_admin_log(chat_id, limit=50)

Message Mirroring

from spluspy.mirror import MessageMirror

mirror = MessageMirror(bot)

# Add a mirroring route
mirror.add_route(
    source=-1001234567890,
    targets=[-1009876543210, -1001112223334],
    strip_forward=True,
    strip_sender=False,
    add_prefix="[Mirror]"
)

# Or build incrementally
mirror.add_source(-1001234567890)
mirror.add_target(-1009876543210)

await mirror.start()

Scheduler

from spluspy.scheduler.scheduler import MessageScheduler

scheduler = MessageScheduler(bot)

# Send a message every hour
scheduler.schedule_interval("hourly_greeting", chat_id, "Hello!", interval=3600)

# Send a message after a delay
scheduler.schedule_once("reminder", chat_id, "Don't forget!", delay=300)

# Cancel a scheduled task
scheduler.cancel("hourly_greeting")

Chat Management

await bot.ban_user(chat_id, user_id)
await bot.unban_user(chat_id, user_id)
await bot.mute_user(chat_id, user_id)
await bot.unmute_user(chat_id, user_id)
await bot.pin_message(chat_id, message)
await bot.unpin_message(chat_id, message)
await bot.join_chat(chat_id)
await bot.leave_chat(chat_id)

Error Handling

from spluspy import filters
from spluspy.errors import FloodWait, Unauthorized, BadRequest

@bot.on_message()
async def safe_handler(client, message):
    try:
        await message.reply("Hello!")
    except FloodWait as e:
        await asyncio.sleep(e.seconds)
    except Unauthorized:
        await message.reply("Not authorized!")
    except BadRequest as e:
        print(f"Bad request: {e}")

Global Error Handler

from spluspy.events import ErrorHandlerBuilder

error_handler = ErrorHandlerBuilder()
error_handler.on(FloodWait)(lambda e: print(f"Flood wait: {e.seconds}s"))
error_handler.on(Unauthorized)(lambda e: print("Unauthorized"))

bot.on_error(error_handler.build())

Batch Operations

# Send multiple messages
messages = ["Hello 1", "Hello 2", "Hello 3"]
results = await bot.batch_send(chat_id, messages)

# Delete multiple messages
await bot.batch_delete(chat_id, [msg1, msg2, msg3])

# Forward multiple messages
await bot.batch_forward(chat_id, [msg1, msg2])

File Transfer with Progress

from spluspy.utils import ProgressTracker

# Upload with progress
tracker = ProgressTracker(on_progress=lambda p: print(f"{p.percent}%"))
await bot.send_document(chat_id, "large_file.zip", progress=tracker)

# Download with progress
await message.download(progress=tracker)

CLI

# Run a bot
spluspy run bot.py

# Run with custom session name
spluspy run bot.py --session my_bot

# Get session info
spluspy session-info my_session.session

# Show version
spluspy version

# Validate a bot script for syntax errors
spluspy validate bot.py

Docker

FROM python:3.12-slim

WORKDIR /app

# Install system dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
    gcc \
    && rm -rf /var/lib/apt/lists/*

# Install spluspy
RUN pip install --no-cache-dir spluspy[all]

# Copy your bot script
COPY bot.py .

# Run the bot
CMD ["python", "bot.py"]

Build and run:

docker build -t my-spluspy-bot .
docker run -v ./sessions:/app/sessions my-spluspy-bot

Project Structure

spluspy/
├── __init__.py          # Public API
├── __version__.py       # Version info
├── cli.py               # CLI entry point
├── config.py            # Configuration
├── compat.py            # Compatibility layer
├── afk.py               # AFK auto-responder
├── admin.py             # Chat administration
├── mirror.py            # Message mirroring engine
├── client/              # Client and conversation API
│   ├── client.py        # Main Client class
│   └── conversation.py  # Conversation API
├── models/              # Domain models (Message, User, Chat, Media, etc.)
├── events/              # Event types and builders
├── filters/             # Composable message filters
├── errors/              # Custom exception hierarchy
├── session/             # Session backends (SQLite, Memory, String)
├── network/             # TCP connections and connection pool
├── storage/             # Key-value storage backends
├── plugins/             # Plugin loader
├── middleware/          # Middleware system
├── fsm/                 # Finite state machine
├── scheduler/           # Task scheduler
├── utils/               # Logger, cache, helpers
├── sync/                # Synchronous client wrapper
└── _engine/             # Low-level MTProto engine

Error Hierarchy

SplusPyError
├── SoroushPlusAPIError
│   ├── RPCError
│   │   ├── FloodWait
│   │   ├── Unauthorized
│   │   ├── BadRequest
│   │   │   ├── ChatNotFound
│   │   │   ├── UserNotFound
│   │   │   └── MessageNotFound
│   │   └── ...
│   ├── SessionExpiredError
│   └── SessionError
├── AuthError
├── ValidationError
├── JoinChatError
│   ├── InvalidInviteLinkError
│   ├── InviteLinkExpiredError
│   ├── ChatFullError
│   ├── ChatDeactivatedError
│   └── MembershipRequiredError
└── FloodWaitError (rate limiter)

فارسی

ویژگی‌ها

ویژگی توضیحات
بدون نیاز به API Key کلیدهای سروش پلاس به صورت داخلی
ناهمگام کامل ساخته شده با asyncio پایتون
پشتیبانی از حالت همگام استفاده بدون async/await از طریق spluspy.sync
پشتیبانی ربات و کاربر هر دو نوع حساب
هندلرهای رویدادمحور سیستم رویداد قدرتمند با دکوراتورها
سیستم فیلتر فیلترهای قابل ترکیب (&, |, ~)
دکمه‌های اینلاین و ریپلای کیبوردهای تعاملی
API مکالمه برای جریان‌های تعاملی ربات
FSM (ماشین حالت محدود) مدیریت وضعیت داخلی برای ربات‌ها
سیستم پلاگین بارگذاری پویای پلاگین‌ها
میان‌افزار (Middleware) پردازش قبل/بعد از به‌روزرسانی‌ها
زمان‌بند (Scheduler) زمان‌بندی داخلی وظایف
بک‌اندهای ذخیره‌سازی متعدد حافظه، SQLite، Redis، PostgreSQL
محدودیت نرخ (Rate Limiting) الگوریتم سطل توکن با مدیریت انتظار سیلاب
پاسخ خودکار AFK پاسخگوی هوشمند با محدودیت نرخ به ازای هر چت
مدیریت چت مسدود کردن، بی‌صدا کردن، سنجاق، پاکسازی — API مدیریتی
آینه‌سازی پیام کلون کردن پیام و رسانه به صورت بلادرنگ بین چت‌ها
عملیات دسته‌ای ارسال، حذف، فوروارد چندین پیام به صورت همزمان
انتقال فایل با پیشرفت آپلود/دانلود با ردیابی پیشرفت
لاگ حرفه‌ای لاگرهای ساختاریافته و فضای نام‌دار
نوع‌نویسی در همه جا ایمنی کامل نوع
معماری تمیز اصول SOLID، طراحی ماژولار

پیش‌نیازها

  • پایتون 3.10 به بالا
  • نیازی به کلید API خارجی نیست (کلیدهای سروش پلاس به صورت داخلی)

نصب

pip install spluspy

برای رمزگذاری سریع‌تر:

pip install spluspy[speed]

با بک‌اند Redis:

pip install spluspy[redis]

با بک‌اند PostgreSQL:

pip install spluspy[postgres]

همه وابستگی‌های اختیاری:

pip install spluspy[all]

شروع سریع

ساده‌ترین ربات

from spluspy import Client

bot = Client("my_session")

@bot.on_message()
async def handler(client, message):
    await message.reply("سلام!")

bot.run()

حساب کاربری

from spluspy import Client

client = Client("session_name")

@client.on_message()
async def handler(client, message):
    await message.reply("سلام دنیا!")

async def main():
    await client.start(phone="+98XXXXXXXXXX")
    await client.run_until_disconnected()

import asyncio
asyncio.run(main())

استفاده همگام (بدون Async/Await)

from spluspy.sync import Client

bot = Client("session")

@bot.on_message()
def handler(client, message):
    message.reply("سلام!")

bot.run()

رویدادها

دکوراتور رویداد
@bot.on_message() پیام جدید
@bot.on_edited() ویرایش پیام
@bot.on_callback_query() کلیک دکمه اینلاین
@bot.on_inline_query() کوئری اینلاین
@bot.on_chat_action() پیوستن/خروج/سنجاق
@bot.on_user_update() تغییر وضعیت
@bot.on_message_deleted() حذف پیام
@bot.on_message_read() رسید خواندن
@bot.on_error() مدیریت خطای سراسری

اولویت رویداد

from spluspy.events import HandlerPriority

@bot.on_message(priority=HandlerPriority.FIRST)
async def high_priority_handler(client, message):
    # اول اجرا می‌شود
    pass

@bot.on_message(priority=HandlerPriority.LOW)
async def low_priority_handler(client, message):
    # آخر اجرا می‌شود
    pass

توقف انتشار

@bot.on_message()
async def stopper(client, message):
    if message.text == "/stop":
        message.stop_propagation()
        await message.reply("توقف!")

فیلترها

from spluspy import filters

@bot.on_message(filters.text)                    # فقط متن
@bot.on_message(filters.private)                 # چت‌های خصوصی
@bot.on_message(filters.group)                   # گروه‌ها
@bot.on_message(filters.command("start"))        # دستور /start
@bot.on_message(filters.regex(r"\d+"))           # تطابق با عبارت باقاعده
@bot.on_message(filters.user(123))               # کاربر خاص
@bot.on_message(filters.text & filters.private)  # ترکیبی
@bot.on_message(filters.photo | filters.video)   # عکس یا ویدیو

متدهای پیام

await message.reply("سلام")              # پاسخ
await message.edit("متن جدید")           # ویرایش
await message.delete()                    # حذف
await message.forward(chat_id)            # فوروارد
await message.copy(chat_id)               # کپی (بدون هدر فوروارد)
await message.pin()                       # سنجاق
await message.react("❤️")                 # واکنش
await message.mark_read()                 # علامت خواندن
await message.download()                  # دانلود رسانه
await message.reply_photo("photo.jpg")    # پاسخ با عکس
await message.reply_video("video.mp4")    # پاسخ با ویدیو
await message.reply_document("file.pdf")  # پاسخ با سند

دکمه‌ها

from spluspy import Button

# کیبورد اینلاین
keyboard = Button.build_inline([
    Button.inline("گزینه ۱", b"opt1"),
    Button.inline("گزینه ۲", b"opt2")
])
await bot.send_message(chat_id, "انتخاب کنید:", buttons=keyboard)

# کیبورد ریپلای
kb = Button.build_reply([
    Button.text("منو"),
    Button.text("تنظیمات")
])
await bot.send_message(chat_id, "انتخاب کنید:", buttons=kb)

# حذف کیبورد
await bot.send_message(chat_id, "تمام", buttons=Button.clear())

FSM (ماشین حالت محدود)

from spluspy.fsm import State, StateMachine
from spluspy.storage import MemoryStorage

storage = MemoryStorage()
fsm = StateMachine(storage)

class Form:
    name = State()
    age = State()

@bot.on_message(filters.command("register"))
async def start_register(client, message):
    ctx = fsm.context(message.sender_id)
    await ctx.set_state(Form.name)
    await message.reply("نام شما چیست؟")

@bot.on_message(filters.private)
async def process_form(client, message):
    ctx = fsm.context(message.sender_id)
    state = await ctx.get_state()

    if state == Form.name:
        await ctx.set_data(name=message.text)
        await ctx.set_state(Form.age)
        await message.reply("سن شما چقدر است؟")
    elif state == Form.age:
        data = await ctx.get_data()
        await ctx.reset()
        await message.reply(f"ثبت شد! نام: {data.get('name')}, سن: {message.text}")

FSM پیشرفته: مسیریابی وضعیت با دکوراتور

from spluspy.fsm import State, StateMachine, StateTransition
from spluspy.storage import SQLiteStorage

storage = SQLiteStorage("fsm.db")
sm = StateMachine(storage)

class Registration:
    name = State()
    age = State()
    complete = State()

@sm.state(Registration.name)
async def handle_name(ctx, message):
    await ctx.set(name=message.text)
    return StateTransition(to=Registration.age)

@sm.state(Registration.age)
async def handle_age(ctx, message):
    await ctx.set(age=message.text)
    return StateTransition(to=Registration.complete)

@sm.state(Registration.complete)
async def handle_complete(ctx, message):
    data = await ctx.get()
    await message.reply(f"تمام شد: {data}")
    await ctx.finish()

بک‌اندهای ذخیره‌سازی

from spluspy.storage import MemoryStorage, SQLiteStorage, RedisStorage, PostgresStorage, get_storage

# حافظه (پیش‌فرض)
storage = MemoryStorage()

# SQLite
storage = SQLiteStorage("data.db")

# Redis
storage = RedisStorage(host="localhost", port=6379, db=0)

# PostgreSQL
storage = PostgresStorage(dsn="postgresql://user:pass@localhost/db")

# تابع کارخانه
storage = get_storage("redis", host="localhost")

سیستم پلاگین

# plugins/hello.py
def register(client):
    @client.on_message(filters.command("hello"))
    async def hello_handler(client, message):
        await message.reply("سلام از پلاگین!")
# main.py
from spluspy import Client

bot = Client("session")
bot.plugins.load("plugins")
bot.run()

میان‌افزار (Middleware)

from spluspy.middleware import Middleware

class LoggingMiddleware(Middleware):
    async def on_update(self, update, handler):
        print(f"به‌روزرسانی دریافت شد: {update}")
        result = await handler(update)
        print(f"هندلر تکمیل شد")
        return result

bot.middleware.add(LoggingMiddleware())

میان‌افزار محدودیت نرخ

from spluspy.middleware import RateLimitMiddleware

# ۱۰ پیام در دقیقه
rate_limit = RateLimitMiddleware(max_messages=10, window_seconds=60)
bot.middleware.add(rate_limit)

محدودیت نرخ

from spluspy.utils import RateLimiter

limiter = RateLimiter(max_calls=10, period=60)

@bot.on_message()
async def limited_handler(client, message):
    if not limiter.allow():
        await message.reply("محدودیت نرخ! دوباره تلاش کنید.")
        return
    await message.reply("باشه")

کش

from spluspy.utils import LRUCache

cache = LRUCache(maxsize=1000, ttl=300)  # TTL ۵ دقیقه‌ای
cache.set("key", "value")
value = cache.get("key")
stats = cache.stats()  # {'hits': 42, 'misses': 3}

پاسخ خودکار AFK

from spluspy import Client, filters
from spluspy.afk import AfkManager

bot = Client("my_account")
afk = AfkManager(bot, message="الان AFK هستم. زود برمی‌گردم!")

@bot.on_message(filters.command("afk"))
async def set_afk(client, message):
    afk.set_afk(True, reason="ناهار")
    await message.reply("الان AFK هستم!")

@bot.on_message(filters.command("back"))
async def unset_afk(client, message):
    afk.set_afk(False)
    await message.reply(f"برگشتم! {afk.total_replies} پاسخ خودکار ارسال شد.")

@bot.on_message(filters.private & filters.incoming)
async def auto_reply(client, message):
    if afk.is_afk:
        await afk.handle(message)

مدیریت چت

from spluspy.admin import ChatAdmin

admin = ChatAdmin(bot)

await admin.ban_user(chat_id, user_id)      # مسدود کردن
await admin.unban_user(chat_id, user_id)    # رفع مسدودیت
await admin.mute_user(chat_id, user_id)     # بی‌صدا کردن
await admin.unmute_user(chat_id, user_id)   # رفع بی‌صدایی
await admin.pin_message(chat_id, message)   # سنجاق کردن
await admin.unpin_message(chat_id, message) # رفع سنجاق
await admin.unpin_all(chat_id)              # رفع همه سنجاق‌ها
await admin.purge_messages(chat_id, limit=100) # پاکسازی پیام‌ها

# عملیات دسته‌ای
await admin.bulk_action(chat_id, "ban", [user_id1, user_id2])

# لاگ مدیریتی
events = await admin.get_admin_log(chat_id, limit=50)

آینه‌سازی پیام

from spluspy.mirror import MessageMirror

mirror = MessageMirror(bot)

# افزودن مسیر آینه‌سازی
mirror.add_route(
    source=-1001234567890,
    targets=[-1009876543210, -1001112223334],
    strip_forward=True,
    strip_sender=False,
    add_prefix="[Mirror]"
)

# یا به صورت تدریجی
mirror.add_source(-1001234567890)
mirror.add_target(-1009876543210)

await mirror.start()

زمان‌بند

from spluspy.scheduler.scheduler import MessageScheduler

scheduler = MessageScheduler(bot)

# ارسال پیام هر ساعت
scheduler.schedule_interval("hourly_greeting", chat_id, "سلام!", interval=3600)

# ارسال پیام با تاخیر
scheduler.schedule_once("reminder", chat_id, "فراموش نکن!", delay=300)

# لغو وظیفه زمان‌بندی شده
scheduler.cancel("hourly_greeting")

مدیریت خطا

from spluspy import filters
from spluspy.errors import FloodWait, Unauthorized, BadRequest

@bot.on_message()
async def safe_handler(client, message):
    try:
        await message.reply("سلام!")
    except FloodWait as e:
        await asyncio.sleep(e.seconds)
    except Unauthorized:
        await message.reply("غیرمجاز!")
    except BadRequest as e:
        print(f"درخواست نادرست: {e}")

هندل خطای سراسری

from spluspy.events import ErrorHandlerBuilder

error_handler = ErrorHandlerBuilder()
error_handler.on(FloodWait)(lambda e: print(f"انتظار سیلاب: {e.seconds} ثانیه"))
error_handler.on(Unauthorized)(lambda e: print("غیرمجاز"))

bot.on_error(error_handler.build())

عملیات دسته‌ای

# ارسال چندین پیام
messages = ["سلام ۱", "سلام ۲", "سلام ۳"]
results = await bot.batch_send(chat_id, messages)

# حذف چندین پیام
await bot.batch_delete(chat_id, [msg1, msg2, msg3])

# فوروارد چندین پیام
await bot.batch_forward(chat_id, [msg1, msg2])

انتقال فایل با پیشرفت

from spluspy.utils import ProgressTracker

# آپلود با پیشرفت
tracker = ProgressTracker(on_progress=lambda p: print(f"{p.percent}%"))
await bot.send_document(chat_id, "فایل_بزرگ.zip", progress=tracker)

# دانلود با پیشرفت
await message.download(progress=tracker)

رابط خط فرمان (CLI)

# اجرای ربات
spluspy run bot.py

# اجرا با نشست سفارشی
spluspy run bot.py --session my_bot

# اطلاعات نشست
spluspy session-info my_session.session

# نمایش نسخه
spluspy version

# اعتبارسنجی اسکریپت ربات
spluspy validate bot.py

Docker

FROM python:3.12-slim

WORKDIR /app

# نصب وابستگی‌های سیستمی
RUN apt-get update && apt-get install -y --no-install-recommends \
    gcc \
    && rm -rf /var/lib/apt/lists/*

# نصب spluspy
RUN pip install --no-cache-dir spluspy[all]

# کپی اسکریپت ربات
COPY bot.py .

# اجرای ربات
CMD ["python", "bot.py"]

ساخت و اجرا:

docker build -t my-spluspy-bot .
docker run -v ./sessions:/app/sessions my-spluspy-bot

ساختار پروژه

spluspy/
├── __init__.py          # API عمومی
├── __version__.py       # اطلاعات نسخه
├── cli.py               # نقطه ورود CLI
├── config.py            # پیکربندی
├── compat.py            # لایه سازگاری
├── afk.py               # پاسخگوی خودکار AFK
├── admin.py             # مدیریت چت
├── mirror.py            # موتور آینه‌سازی پیام
├── client/              # کلاینت و API مکالمه
│   ├── client.py        # کلاس اصلی Client
│   └── conversation.py  # API مکالمه
├── models/              # مدل‌های دامنه (Message, User, Chat, Media, و غیره)
├── events/              # انواع رویداد و بیلدرها
├── filters/             # فیلترهای قابل ترکیب پیام
├── errors/              # سلسله مراتب استثنای سفارشی
├── session/             # بک‌اندهای نشست (SQLite, Memory, String)
├── network/             # اتصالات TCP و استخر اتصال
├── storage/             # بک‌اندهای ذخیره‌سازی کلید-مقدار
├── plugins/             # بارگذار پلاگین
├── middleware/          # سیستم میان‌افزار
├── fsm/                 # ماشین حالت محدود
├── scheduler/           # زمان‌بند وظایف
├── utils/               # لاگر، کش، کمک‌کننده‌ها
├── sync/                # کلاینت همگام‌سازی شده
└── _engine/             # موتور MTProto سطح پایین

سلسله مراتب خطاها

SplusPyError
├── SoroushPlusAPIError
│   ├── RPCError
│   │   ├── FloodWait
│   │   ├── Unauthorized
│   │   ├── BadRequest
│   │   │   ├── ChatNotFound
│   │   │   ├── UserNotFound
│   │   │   └── MessageNotFound
│   │   └── ...
│   ├── SessionExpiredError
│   └── SessionError
├── AuthError
├── ValidationError
├── JoinChatError
│   ├── InvalidInviteLinkError
│   ├── InviteLinkExpiredError
│   ├── ChatFullError
│   ├── ChatDeactivatedError
│   └── MembershipRequiredError
└── FloodWaitError (محدودیت نرخ)

Contributing / مشارکت

English

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/your-feature)
  3. Set up the development environment:
    python -m venv venv
    source venv/bin/activate
    pip install -e ".[dev]"
    
  4. Make your changes
  5. Run linting and formatting:
    ruff check spluspy/
    black spluspy/
    
  6. Run type checking:
    mypy spluspy/
    
  7. Run tests: pytest
  8. Submit a pull request

فارسی

  1. مخزن را Fork کنید
  2. شاخه ویژگی بسازید (git checkout -b feature/your-feature)
  3. محیط توسعه را راه‌اندازی کنید:
    python -m venv venv
    source venv/bin/activate
    pip install -e ".[dev]"
    
  4. تغییرات خود را اعمال کنید
  5. لینتر و فرمت‌کننده را اجرا کنید:
    ruff check spluspy/
    black spluspy/
    
  6. بررسی نوع را اجرا کنید:
    mypy spluspy/
    
  7. تست‌ها را اجرا کنید: pytest
  8. درخواست Pull ارسال کنید

License / مجوز

MIT License — see LICENSE for details.

مجوز MIT — جزئیات را در LICENSE مشاهده کنید.


Disclaimer / سلب مسئولیت

English: SPlusPy is an unofficial third-party library. Use it responsibly and ensure your applications comply with Soroush Plus's Terms of Service.

فارسی: SPlusPy یک کتابخانه غیررسمی و شخص ثالث است. مسئولانه از آن استفاده کنید و مطمئن شوید برنامه‌های شما با شرایط استفاده سروش پلاس مطابقت دارند.


Made with ❤️ for the Soroush Plus community

ساخته شده با ❤️ برای جامعه سروش پلاس

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

spluspy-2.2.1.tar.gz (737.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

spluspy-2.2.1-py3-none-any.whl (819.9 kB view details)

Uploaded Python 3

File details

Details for the file spluspy-2.2.1.tar.gz.

File metadata

  • Download URL: spluspy-2.2.1.tar.gz
  • Upload date:
  • Size: 737.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.4

File hashes

Hashes for spluspy-2.2.1.tar.gz
Algorithm Hash digest
SHA256 ade022d8b2586482651b902b71ad1ade2c1712366bff0a8b6bb626a77e118347
MD5 1a9f3d8c3926693c7655b235270c40bc
BLAKE2b-256 b43a300d4cb54329e3f38df3cad50726aec1e13907b773c284343c33e9dd5123

See more details on using hashes here.

File details

Details for the file spluspy-2.2.1-py3-none-any.whl.

File metadata

  • Download URL: spluspy-2.2.1-py3-none-any.whl
  • Upload date:
  • Size: 819.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.4

File hashes

Hashes for spluspy-2.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 996f265be18cb55a0f824d4c1ca0d2c1c70c7333f5d3d8526b535711f59d998b
MD5 27ff299cc5fa6b196a9a1f5e8d0b65d7
BLAKE2b-256 d869c3550581bc4e26552f9bd1524da4c9e1b2ff3194e98e89b0bde92f0e3c86

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page