Skip to main content

Soroush Bot

Official Soroush Plus Bot Library

This is the official library for developing bots on the Soroush Plus platform, developed by the Soroush Plus team with programming by Mahdi Ahmadi. A comprehensive Python library for the Soroush Plus Bot API, designed with a clean and intuitive interface inspired by aiogram and rubka.

Python Version License Version


Features

  • 🚀 Full API Coverage - All methods from the official Soroush Plus Bot API documentation
  • 🎨 Intuitive Design - Clean and Pythonic interface similar to aiogram and rubka
  • ⌨️ Rich Keyboard Support - Both inline and reply keyboards with easy builders
  • 🔍 Powerful Filters - Command, text, media, chat type, and custom filters
  • 📦 Complete Type System - Full type hints and data classes for all API objects
  • 🔄 Long Polling & Webhooks - Support for both update retrieval methods
  • 📁 File Upload Support - Upload photos, documents, audio, video, and more
  • 🛡️ Error Handling - Comprehensive exception handling and error management

Installation

pip install aiohttp

Or install directly from source:

git clone https://github.com/yourusername/soroush-bot.git
cd soroush-bot
pip install -e .

Quick Start

import asyncio
from sorosh import Robot
from sorosh.filters import CommandFilter
from sorosh.types import ChatKeypadBuilder, InlineKeyboardBuilder

# Initialize your bot
TOKEN = "YOUR_BOT_TOKEN"
bot = Robot(TOKEN, show_progress=True)

# Build a keyboard
def make_main_menu():
    kb = ChatKeypadBuilder(resize_keyboard=True)
    kb.button("📸 Photo", id="photo")
    kb.button("🎲 Dice", id="dice")
    kb.row()
    kb.button("ℹ️ Info", id="info")
    return kb.build()

# Handle /start command
@bot.on_message(CommandFilter(["start", "help"]))
async def start_handler(bot, message):
    inline_kb = InlineKeyboardBuilder()
    inline_kb.callback("About", "about")
    inline_kb.url("Website", "https://splus.ir")
    
    await message.reply(
        "👋 Welcome to the Soroush Plus Bot!\n\n"
        "Use the buttons below to get started:",
        chat_keypad=make_main_menu(),
        inline_keypad=inline_kb.build()
    )

# Handle callback queries
@bot.on_callback("about")
async def about_callback(bot, message):
    await message.answer(
        "🤖 Soroush Plus Bot\n"
        "Built with Soroush Bot Library",
        show_alert=True
    )

# Default handler for unknown messages
@bot.default
async def default_handler(bot, message):
    await message.reply(
        "❌ I didn't understand that.\n"
        "Use /help to see available commands.",
        chat_keypad=make_main_menu()
    )

# Run the bot
async def main():
    await bot.run()

if __name__ == "__main__":
    asyncio.run(main())

Table of Contents

Core Components

Bot / Robot

The Robot class (alias for Bot) is the main entry point for your bot.

from sorosh import Robot

bot = Robot(TOKEN, show_progress=True)

Parameters:

  • token (str): Your bot token from @botfather
  • show_progress (bool): Show progress messages in console (default: False)
  • session (aiohttp.ClientSession): Custom session (optional)

Message

The Message class represents a message received from the API. It provides rich properties and methods for interacting with messages.

Key Properties:

  • text - Message text
  • caption - Media caption
  • chat_id - Chat identifier
  • sender_id - Sender identifier
  • message_id - Message identifier
  • from_user - Sender user object
  • chat - Chat object
  • is_command - Whether the message is a command
  • is_private - Whether the chat is private
  • is_group - Whether the chat is a group
  • is_photo - Whether the message contains a photo
  • is_video - Whether the message contains a video
  • is_audio - Whether the message contains audio
  • is_document - Whether the message contains a document
  • is_sticker - Whether the message contains a sticker
  • is_voice - Whether the message contains a voice message
  • is_animation - Whether the message contains an animation (GIF)
  • is_location - Whether the message contains a location
  • is_contact - Whether the message contains a contact
  • is_dice - Whether the message contains a dice
  • has_media - Whether the message has any media
  • is_reply - Whether the message is a reply
  • is_forwarded - Whether the message is forwarded
  • is_edited - Whether the message is edited
  • has_link - Whether the text contains a link
  • is_hashtag - Whether the text contains a hashtag
  • is_emoji - Whether the text contains emojis
  • args - Command arguments as a list

Filters

Filters are used to handle specific types of messages.

from sorosh.filters import CommandFilter, TextFilter, PrivateFilter, HasPhotoFilter

@bot.on_message(CommandFilter(["start"]))
async def start_handler(bot, message):
    # Handle /start command
    pass

@bot.on_message(TextFilter(r"^hello$"))
async def hello_handler(bot, message):
    # Handle "hello" text
    pass

@bot.on_message(PrivateFilter())
async def private_handler(bot, message):
    # Handle messages from private chats
    pass

@bot.on_message(HasPhotoFilter())
async def photo_handler(bot, message):
    # Handle messages with photos
    pass

Keyboards

Reply Keyboard (ChatKeypadBuilder)

Builds reply keyboards (buttons below the input field).

from sorosh.types import ChatKeypadBuilder

def make_keyboard():
    kb = ChatKeypadBuilder(resize_keyboard=True)
    kb.button("Button 1", id="btn1")
    kb.button("Button 2", id="btn2")
    kb.row()
    kb.button("Button 3", id="btn3")
    return kb.build()

# Usage
await message.reply("Choose an option:", chat_keypad=make_keyboard())

Inline Keyboard (InlineKeyboardBuilder)

Builds inline keyboards (buttons below the message).

from sorosh.types import InlineKeyboardBuilder

def make_inline_keyboard():
    kb = InlineKeyboardBuilder()
    kb.callback("Option 1", "opt1")
    kb.callback("Option 2", "opt2")
    kb.row()
    kb.url("Website", "https://example.com")
    return kb.build()

# Usage
await message.reply("Choose an option:", inline_keypad=make_inline_keyboard())

API Methods

Send Messages

send_message()

Send a text message.

await bot.send_message(
    chat_id=message.chat_id,
    text="Hello, world!",
    parse_mode=None,  # Optional: "HTML" or "Markdown"
    chat_keypad=keyboard,  # Reply keyboard
    inline_keypad=inline_keyboard,  # Inline keyboard
    reply_to_message_id=message.message_id  # Reply to specific message
)

Send Media

send_photo()

Send a photo from URL, bytes, or file_id.

# From URL
await bot.send_photo(
    chat_id=message.chat_id,
    photo="https://example.com/image.jpg",
    caption="Beautiful photo!",
    inline_keypad=keyboard
)

# From bytes
with open("image.jpg", "rb") as f:
    image_data = f.read()
    await bot.send_photo(
        chat_id=message.chat_id,
        photo=image_data,
        caption="Uploaded photo!"
    )

send_audio()

Send an audio file.

await bot.send_audio(
    chat_id=message.chat_id,
    audio="https://example.com/song.mp3",
    caption="Check out this song!",
    performer="Artist Name",
    title="Song Title",
    duration=180
)

send_document()

Send a document file.

await bot.send_document(
    chat_id=message.chat_id,
    document="https://example.com/document.pdf",
    caption="Important document!"
)

send_video()

Send a video file.

await bot.send_video(
    chat_id=message.chat_id,
    video="https://example.com/video.mp4",
    caption="Watch this video!",
    duration=120,
    width=1280,
    height=720
)

send_animation()

Send an animation (GIF).

await bot.send_animation(
    chat_id=message.chat_id,
    animation="https://example.com/animation.gif",
    caption="Funny GIF!"
)

send_voice()

Send a voice message.

await bot.send_voice(
    chat_id=message.chat_id,
    voice="https://example.com/voice.ogg",
    caption="Voice message",
    duration=30
)

send_video_note()

Send a video note (round video).

await bot.send_video_note(
    chat_id=message.chat_id,
    video_note="https://example.com/video_note.mp4",
    duration=10,
    length=240
)

Send Special Messages

send_location()

Send a location.

await bot.send_location(
    chat_id=message.chat_id,
    latitude=35.6892,
    longitude=51.3890
)

send_contact()

Send a contact.

await bot.send_contact(
    chat_id=message.chat_id,
    phone_number="+989123456789",
    first_name="John",
    last_name="Doe"
)

send_dice()

Send a dice.

await bot.send_dice(
    chat_id=message.chat_id,
    emoji="🎲"  # Optional: 🎲, 🎯, 🏀, ⚽, 🎳
)

send_sticker()

Send a sticker.

await bot.send_sticker(
    chat_id=message.chat_id,
    sticker="https://example.com/sticker.webp"
)

send_poll()

Send a poll.

await bot.send_poll(
    chat_id=message.chat_id,
    question="What is your favorite color?",
    options=["Red", "Blue", "Green", "Yellow"],
    type="Regular",  # or "Quiz"
    allows_multiple_answers=True,
    is_anonymous=True,
    correct_option_index=0,  # For Quiz type
    hint="Hint for the quiz"
)

Edit Messages

edit_message_text()

Edit a message's text.

await bot.edit_message_text(
    text="New text",
    chat_id=message.chat_id,
    message_id=message.message_id
)

edit_message_caption()

Edit a message's caption.

await bot.edit_message_caption(
    chat_id=message.chat_id,
    message_id=message.message_id,
    caption="New caption"
)

edit_message_reply_markup()

Edit a message's keyboard.

await bot.edit_message_reply_markup(
    chat_id=message.chat_id,
    message_id=message.message_id,
    reply_markup=new_keyboard.to_dict()
)

Delete Messages

delete_message()

Delete a message.

await bot.delete_message(
    chat_id=message.chat_id,
    message_id=message.message_id
)

Reply Methods

The Message object provides convenient reply methods:

# Reply with text
await message.reply("Hello!")

# Reply with photo
await message.reply_image("https://example.com/image.jpg", caption="Photo!")

# Reply with location
await message.reply_location(35.6892, 51.3890)

# Reply with dice
await message.reply_dice("🎲")

# Reply with contact
await message.reply_contact("+989123456789", "John", "Doe")

# Reply with audio
await message.reply_audio("https://example.com/song.mp3")

# Reply with document
await message.reply_document("https://example.com/file.pdf")

# Reply with video
await message.reply_video("https://example.com/video.mp4")

# Reply with animation
await message.reply_animation("https://example.com/animation.gif")

# Reply with voice
await message.reply_voice("https://example.com/voice.ogg")

# Reply with sticker
await message.reply_sticker("https://example.com/sticker.webp")

# Edit the message
await message.edit("New text")

# Delete the message
await message.delete()

Working with Updates

Long Polling

Start the bot with long polling:

async def main():
    await bot.run(timeout=30, limit=100)

if __name__ == "__main__":
    asyncio.run(main())

Parameters:

  • timeout (int): Long polling timeout in seconds (default: 30)
  • limit (int): Maximum number of updates to fetch (default: 100)

Webhooks

Set up a webhook:

await bot.set_webhook(
    url="https://example.com/webhook",
    max_connections=40,
    allowed_updates=["message", "callback_query"]
)

Delete a webhook:

await bot.delete_webhook()

Get webhook info:

info = await bot.get_webhook_info()
print(info.url)
print(info.pending_update_count)

Filters

Command Filter

Handle specific commands:

@bot.on_message(CommandFilter(["start", "help"]))
async def handler(bot, message):
    # Handles /start and /help
    pass

Text Filter

Handle text matching a regex pattern:

@bot.on_message(TextFilter(r"^hello$"))
async def handler(bot, message):
    # Handles exact "hello" text
    pass

@bot.on_message(TextFilter(r"(hello|hi|hey)"))
async def handler(bot, message):
    # Handles hello, hi, or hey
    pass

Media Filters

Handle messages containing specific media types:

from sorosh.filters import (
    HasPhotoFilter,
    HasAudioFilter,
    HasDocumentFilter,
    HasVideoFilter,
    HasAnimationFilter,
    HasVoiceFilter,
    HasStickerFilter,
    HasLocationFilter,
    HasContactFilter,
)

@bot.on_message(HasPhotoFilter())
async def photo_handler(bot, message):
    # Handles messages with photos
    pass

@bot.on_message(HasVideoFilter())
async def video_handler(bot, message):
    # Handles messages with videos
    pass

Chat Type Filters

Handle messages from specific chat types:

from sorosh.filters import PrivateFilter, GroupFilter, ChannelFilter

@bot.on_message(PrivateFilter())
async def private_handler(bot, message):
    # Handles private chat messages
    pass

@bot.on_message(GroupFilter())
async def group_handler(bot, message):
    # Handles group messages
    pass

Combining Filters

Combine multiple filters using AndFilter and OrFilter:

from sorosh.filters import AndFilter, OrFilter, CommandFilter, PrivateFilter

# Both conditions must be true
@bot.on_message(AndFilter(CommandFilter(["start"]), PrivateFilter()))
async def handler(bot, message):
    # Handles /start in private chats only
    pass

# At least one condition must be true
@bot.on_message(OrFilter(CommandFilter(["help"]), TextFilter(r"^help$")))
async def handler(bot, message):
    # Handles /help or "help" text
    pass

Types Reference

User

Represents a user or bot.

Properties:

  • id (int): Unique identifier
  • is_bot (bool): Whether the user is a bot
  • first_name (str): First name
  • last_name (str, optional): Last name
  • username (str, optional): Username
  • language_code (str, optional): Language code
  • full_name (str): Full name

Chat

Represents a chat.

Properties:

  • id (int): Unique identifier
  • type (str): Chat type ("private", "group", "supergroup", "channel")
  • title (str, optional): Chat title
  • username (str, optional): Username
  • first_name (str, optional): First name (private chats)
  • last_name (str, optional): Last name (private chats)

Update

Represents an incoming update.

Properties:

  • update_id (int): Unique update identifier
  • message (Message, optional): New message
  • edited_message (Message, optional): Edited message
  • callback_query (CallbackQuery, optional): Callback query
  • has_message (bool): Whether update contains a message
  • has_callback_query (bool): Whether update contains a callback query

CallbackQuery

Represents a callback query from an inline button.

Properties:

  • id (str): Unique identifier
  • from_user (User): Sender
  • message (Message, optional): Message with the inline button
  • data (str, optional): Callback data

File

Represents a file ready for download.

Properties:

  • file_id (str): File identifier
  • file_unique_id (str): Unique file identifier
  • file_size (int, optional): File size in bytes
  • file_path (str, optional): File path

Methods:

  • get_download_url(token) → Get download URL

Error Handling

The library provides specific exception types:

from sorosh.exceptions import SoroushAPIError, SoroushError, ValidationError, NetworkError

try:
    await bot.send_message(chat_id, text)
except SoroushAPIError as e:
    print(f"API Error: {e.message} (Code: {e.error_code})")
except SoroushError as e:
    print(f"General Error: {e}")
except ValidationError as e:
    print(f"Validation Error: {e}")
except NetworkError as e:
    print(f"Network Error: {e}")

Common Error Codes

Code Description
400 Bad Request
401 Unauthorized (invalid token)
403 Forbidden
404 Not Found
429 Too Many Requests
500 Internal Server Error

Examples

Basic Echo Bot

import asyncio
from sorosh import Robot
from sorosh.filters import CommandFilter, TextFilter

TOKEN = "YOUR_TOKEN"
bot = Robot(TOKEN, show_progress=True)

@bot.on_message(CommandFilter(["start"]))
async def start_handler(bot, message):
    await message.reply("👋 Hello! Send me any message and I'll echo it back.")

@bot.on_message(TextFilter(r".*"))
async def echo_handler(bot, message):
    await message.reply(f"🔊 {message.text}")

async def main():
    await bot.run()

if __name__ == "__main__":
    asyncio.run(main())

Admin Panel with Statistics

import asyncio
from sorosh import Robot
from sorosh.filters import CommandFilter, PrivateFilter, TextFilter
from sorosh.types import ChatKeypadBuilder

TOKEN = "YOUR_TOKEN"
ADMIN_IDS = ["your_admin_id_here"]
bot = Robot(TOKEN, show_progress=True)

user_stats = {}

def admin_keyboard():
    kb = ChatKeypadBuilder(resize_keyboard=True)
    kb.button("📊 Stats", id="stats")
    kb.button("📢 Broadcast", id="broadcast")
    kb.row()
    kb.button("👥 Users", id="users")
    kb.button("🗑️ Clear", id="clear")
    return kb.build()

@bot.on_message(CommandFilter(["admin"]), PrivateFilter())
async def admin_handler(bot, message):
    if message.sender_id not in ADMIN_IDS:
        await message.reply("❌ Unauthorized!")
        return
    
    await message.reply(
        "🔐 Admin Panel\n\n"
        "Choose an option:",
        chat_keypad=admin_keyboard()
    )

@bot.on_message(TextFilter(r"^📊 Stats$"))
async def stats_handler(bot, message):
    if message.sender_id not in ADMIN_IDS:
        return
    
    total_users = len(user_stats)
    total_messages = sum(data.get("count", 0) for data in user_stats.values())
    
    await message.reply(
        f"📊 Statistics\n\n"
        f"👥 Total Users: {total_users}\n"
        f"📨 Total Messages: {total_messages}"
    )

@bot.default
async def track_messages(bot, message):
    if message.sender_id:
        if message.sender_id not in user_stats:
            user_stats[message.sender_id] = {"count": 0}
        user_stats[message.sender_id]["count"] += 1

async def main():
    await bot.run()

if __name__ == "__main__":
    asyncio.run(main())

Poll Bot

import asyncio
from sorosh import Robot
from sorosh.filters import CommandFilter
from sorosh.types import InlineKeyboardBuilder

TOKEN = "YOUR_TOKEN"
bot = Robot(TOKEN, show_progress=True)

@bot.on_message(CommandFilter(["poll"]))
async def poll_handler(bot, message):
    kb = InlineKeyboardBuilder()
    kb.callback("📊 View Results", "poll_results")
    
    await bot.send_poll(
        chat_id=message.chat_id,
        question="What is your favorite programming language?",
        options=["Python", "JavaScript", "Go", "Rust", "Other"],
        type="Regular",
        allows_multiple_answers=False,
        is_anonymous=True,
        inline_keypad=kb.build(),
        reply_to_message_id=message.message_id
    )

@bot.on_callback("poll_results")
async def poll_results_callback(bot, message):
    await message.answer(
        "📊 Current Results\n\n"
        "Python: 45%\n"
        "JavaScript: 25%\n"
        "Go: 15%\n"
        "Rust: 10%\n"
        "Other: 5%",
        show_alert=True
    )

async def main():
    await bot.run()

if __name__ == "__main__":
    asyncio.run(main())
import asyncio
import random
from sorosh import Robot
from sorosh.filters import CommandFilter, TextFilter
from sorosh.types import ChatKeypadBuilder, InlineKeyboardBuilder

TOKEN = "YOUR_TOKEN"
bot = Robot(TOKEN, show_progress=True)

# Sample images
IMAGES = [
    "https://cdn.pixabay.com/photo/2024/01/15/10/00/cat-8509285_640.jpg",
    "https://cdn.pixabay.com/photo/2023/12/16/11/23/dog-8452478_640.jpg",
    "https://cdn.pixabay.com/photo/2023/12/11/16/35/bird-8444123_640.jpg",
]

def gallery_keyboard():
    kb = ChatKeypadBuilder(resize_keyboard=True)
    kb.button("🎲 Random Photo", id="random")
    kb.button("📸 Next Photo", id="next")
    return kb.build()

@bot.on_message(CommandFilter(["gallery"]))
async def gallery_handler(bot, message):
    kb = InlineKeyboardBuilder()
    kb.callback("❤️ Like", "like")
    kb.callback("🔄 Next", "next_photo")
    
    await bot.send_photo(
        chat_id=message.chat_id,
        photo=random.choice(IMAGES),
        caption="📸 Random Photo\n\nClick the buttons below!",
        inline_keypad=kb.build(),
        chat_keypad=gallery_keyboard()
    )

@bot.on_message(TextFilter(r"^🎲 Random Photo$"))
async def random_photo_handler(bot, message):
    await gallery_handler(bot, message)

@bot.on_callback("like")
async def like_callback(bot, message):
    await message.answer("❤️ Photo liked!", show_alert=False)

@bot.on_callback("next_photo")
async def next_photo_callback(bot, message):
    await message.answer("🔄 Loading next photo...", show_alert=False)
    await gallery_handler(bot, message)

async def main():
    await bot.run()

if __name__ == "__main__":
    asyncio.run(main())

Contributing

Contributions are welcome! Here's how you can help:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Development Setup

# Clone the repository
git clone https://github.com/yourusername/soroush-bot.git
cd soroush-bot

# Create a virtual environment
python -m venv venv
source venv/bin/activate  # On Windows: venv\Scripts\activate

# Install dependencies
pip install -e .
pip install pytest pytest-asyncio

# Run tests
pytest

License

MIT License

Copyright (c) 2024 Soroush Bot Contributors

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


Quick Reference

Common Methods

Method Description
bot.run() Start long polling
bot.send_message() Send text message
bot.send_photo() Send photo
bot.send_audio() Send audio file
bot.send_document() Send document
bot.send_video() Send video
bot.send_animation() Send animation (GIF)
bot.send_voice() Send voice message
bot.send_sticker() Send sticker
bot.send_location() Send location
bot.send_contact() Send contact
bot.send_dice() Send dice
bot.send_poll() Send poll
bot.edit_message_text() Edit message text
bot.edit_message_caption() Edit message caption
bot.delete_message() Delete message

Message Reply Methods

Method Description
message.reply() Reply with text
message.reply_image() Reply with image
message.reply_photo() Reply with photo
message.reply_audio() Reply with audio
message.reply_document() Reply with document
message.reply_video() Reply with video
message.reply_animation() Reply with animation
message.reply_voice() Reply with voice
message.reply_sticker() Reply with sticker
message.reply_location() Reply with location
message.reply_contact() Reply with contact
message.reply_dice() Reply with dice
message.reply_poll() Reply with poll
message.edit() Edit message
message.delete() Delete message

Keyboard Builders

Builder Description
ChatKeypadBuilder Build reply keyboards
InlineKeyboardBuilder Build inline keyboards
ReplyKeyboardRemove Remove reply keyboard

Filters

Filter Description
CommandFilter() Filter by command
TextFilter() Filter by text pattern
PrivateFilter() Filter private chats
GroupFilter() Filter groups
ChannelFilter() Filter channels
HasPhotoFilter() Filter messages with photos
HasAudioFilter() Filter messages with audio
HasDocumentFilter() Filter messages with documents
HasVideoFilter() Filter messages with videos
HasAnimationFilter() Filter messages with animations
HasVoiceFilter() Filter messages with voice
HasStickerFilter() Filter messages with stickers
HasLocationFilter() Filter messages with locations
HasContactFilter() Filter messages with contacts
AndFilter() Combine filters with AND
OrFilter() Combine filters with OR

Metadata

Release files for sorosh 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for sorosh 1.0.0
File Size Uploaded
sorosh-1.0.0.tar.gz 38.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for sorosh 1.0.0
File Interpreter ABI Platform
sorosh-1.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 76.1 kB

Release files / sorosh-1.0.0.tar.gz

Download URL sorosh-1.0.0.tar.gz
Size 38.9 kB
Tags Source
SHA-256 checksum
How to use checksums
407557122c84acb6d4bfc74b7478a20109ff9f163c4397153aa9328f06aaef01
BLAKE2b-256 checksum
How to use checksums
22314d2013366bac00d36f92a10d50e303120156da5032da002a3e850aaed73a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.11.4

Release files / sorosh-1.0.0-py3-none-any.whl

Download URL sorosh-1.0.0-py3-none-any.whl
Size 37.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b8cefd43c5f529fc30720d1f222dc08d33bca8b7285259e4a4092c330154379b
BLAKE2b-256 checksum
How to use checksums
31ab8403ec0ef9eeef57c3b16293121a3a355dbf7f8bb4c1f16564b92ddc8e85
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.11.4

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page