Metadata-Version: 2.4
Name: redzedbot
Version: 1.0.0
Summary: FreeFire Async TCP Bot ! , Fast , Safe , Legal , Ethical , By RedZedKing
License: MIT
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE.txt
Requires-Dist: aiohttp
Requires-Dist: pycryptodome
Requires-Dist: protobuf
Requires-Dist: google-play-scraper
Requires-Dist: requests
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"
Requires-Dist: pytest-asyncio; extra == "dev"
Dynamic: license-file

# redzedbot

An async Python library for Free Fire bot sessions — clean API, auto-reconnect, and event-driven message handling.

---

## Installation

```bash
pip install redzedbot
```

---

## Quick start

```python
import asyncio
from redzedbot import guest

async def main():
    bot = await guest.login('UID', 'PASSWORD') #login
    if bot: #if logged in successfully
        print(bot._token) #print JWT
        @bot.online() #bot online handler
        def on_ready(bot):
            if bot.is_connected: #if bot is connected to online tcp 
                print(bot.nickname) #show bot name
                print(f'''
                {'-'*20}
                Bot Online ! .
                Bot Name : {bot.nickname}
                Region : {bot.region}
                Uid : {bot.account_uid}
                {'-'*20}
                ''') #just to explain more .
                

    @bot.message_handler(command='hello') #two commands handlers .
    async def hello(msg):
        user_id = msg.user_id
        await bot.send_dm('Hello There ! ', user_id)


    @bot.message_handler(command='king')
    async def reply(msg):
        uid = msg.user_id
        name = msg.nickname
        if uid == 6728454010: #check if the uid equals to a specific uid to give a diffrent reply / command .
            await bot.send_dm(f'Welcome ! RedZed :/ , {name}',uid)
        else: 
            await bot.send_dm('Lol , not redzed',uid)

    @bot.message_handler(command='add') #one command handler
    async def world(msg):
        user_id = msg.user_id
        txt = msg.text
        parts = txt.strip().split() #split text parts according to spaces .
        if len(parts) < 2: #check if the parts are less than 2 args
            await bot.send_dm('Please Specify A Uid . ', user_id) #if less than 2 args
        else:
            uid = parts[1]
            msg = await bot.add_friend(uid) #proceed to add the uid
            await bot.send_dm(msg , user_id)





    await bot.online_loop() #loop online


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

---

### `Bot` — properties

| Property | Type | Description |
|---|---|---|
| `bot.nickname` | `str` | In-game account name |
| `bot.uid` | `int` | account UID |
| `bot.region` | `str` | Server region (e.g. `"ME"`, `"SEA"`) |
| `bot.clan_id` | `int \| None` | Clan ID, or `None` if not in a clan |
| `bot.is_connected` | `bool` | `True` after the TCP sockets are live |

---

### `Bot` — messaging

```python
await bot.send_squad("hello team")              # team / squad chat
await bot.send_guild("hello clan")              # guild / clan chat  (raises if no clan)
await bot.send_dm(target_uid, "hey there")      # whisper / DM
```

All three raise `RuntimeError` if the bot is not yet connected.

---

### `Bot` — decorators

#### `@bot.online()`

Fires **once**, right after both TCP sockets connect (or reconnect).  Receives the `Bot` instance.  Works with both `async def` and plain `def`.

```python
@bot.online()
def on_ready(bot):
    print(bot.nickname)

# ── or ──

@bot.online()
async def on_ready(bot):
    await bot.send_dm("I'm alive!",123456789)
```

---

#### `@bot.on_message`

Fires for **every** incoming chat / whisper packet.  Receives `(bot, msg)`.

```python
@bot.on_message
async def handler(bot, msg):
    print(msg["sender"], ":", msg["text"])
```

`msg` dict keys:

| Key | Description |
|---|---|
| `uid` | Sender's numeric UID |
| `chat_id` | Chat-room / target UID |
| `type` | Chat type int (squad / guild / DM) |
| `text` | Message body string |
| `sender` | Sender's nickname |
| `avatar` | Sender's profile-pic URL |
---

#### `@bot.on_error`

Fires whenever a TCP connection error occurs **before** the reconnect sleep.  Receives `(bot, exception)`.

```python
@bot.on_error
def on_err(bot, err):
    print("Connection error:", err)
```

---

#### `@bot.on_disconnect`

Fires once after both sockets have been fully closed (e.g. after you call `await bot.disconnect()`).

```python
@bot.on_disconnect
def on_dc(bot):
    print("Disconnected, bye!")
```

---

### `Bot` — lifecycle

```python
await bot.online_loop(reconnect_delay=3.0)   # blocking – runs forever, auto-reconnects
```

`online_loop` manages both the **Online** and **Chat** TCP sockets concurrently.  If either socket drops, both are torn down and re-established after `reconnect_delay` 

---

## Full example — echo bot

```python
import asyncio
from redzedbot import guest

async def main():
    bot = await guest.login("UID", "PASSWORD")
    if not bot:
        print("Login failed"); return

    @bot.online()
    def ready(bot):
        print(f"[{bot.region}] {bot.nickname} is online")

    @bot.on_message
    async def echo(bot, msg):
        text = msg.text
        if text.startswith("!say "):
            await bot.send_dm(text[5:],msg["uid"]) #first one gets the text after 5 letters according to '!say ' to get what the user has typed / second one to get uid
        #or instead of using msg["uid"] you can use : uid = msg.user_id


    @bot.on_error
    def err(bot, e):
        print("err:", e)

    #currently not working , i didn't test it yet

    @bot.on_disconnect
    def dc(bot):
        print("disconnected")
    

    await bot.online_loop()

asyncio.run(main())
```

---

## Notes

* The library is **async-only**.  Everything must run inside `asyncio.run()`.
* SSL certificate verification is deliberately disabled for the Garena
  endpoints (mirrors the original client behaviour).
* `online_loop()` auto-reconnects on socket errors.  Token expiry (≈ 7 h)
  requires a fresh `guest.login()` call.
* All decorators accept both `async def` and plain `def` — the library
  detects and handles both transparently.
