Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 46 additions & 6 deletions .env.sample
Original file line number Diff line number Diff line change
@@ -1,13 +1,53 @@
# Don't use quotes( " and ' )
# Ultroid environment sample
# Copy to .env and fill values. Don't use quotes ( " and ' ).
# Guided setup: python -m pyUltroid setup
# Health check: python -m pyUltroid doctor
#
# Hosted (Heroku / Railway / Fly / …): set the same keys as config vars
# in the dashboard — do not rely on interactive setup there.

# ── Required ──────────────────────────────────────────────
# From https://my.telegram.org (your own app — no shared defaults)
API_ID=
API_HASH=
SESSION=


# [OPTIONAL]
# Telethon/Pyrogram string session (bash sessiongen)
SESSION=

# ── Database (pick one) ───────────────────────────────────
# Redis: host:port only — NO redis:// or https:// prefix
REDIS_URI=
REDIS_PASSWORD=
LOG_CHANNEL=
BOT_TOKEN=

# Railway-style Redis (alternative to REDIS_URI)
# REDISHOST=
# REDISPORT=
# REDISPASSWORD=

# MongoDB
# MONGO_URI=mongodb+srv://user:pass@cluster/...

# PostgreSQL
# DATABASE_URL=postgres://user:pass@host:5432/dbname

# ── Assistant / logging ───────────────────────────────────
# BOT_TOKEN=123456:ABC... # optional; autopilot can create one
# LOG_CHANNEL=-100xxxxxxxxxx # optional; autopilot can create one

# ── Features ──────────────────────────────────────────────
# ADDONS=False
# VCBOT=False
# VC_SESSION=

# ── Hosted extras ─────────────────────────────────────────
# HEROKU_API=
# HEROKU_APP_NAME=
# TGDB_URL=

# ── QoL / safety toggles (hosted-safe) ────────────────────
# ULTROID_AUTO_PIP=1 # runtime pip fallback (default on). Set 0 on locked hosts.
# ULTROID_STRICT_CONFIG=0 # set 1 to require a remote DB at boot
# SKIP_AUTOPILOT=False # set True + provide LOG_CHANNEL yourself
# SKIP_AUTOBOT=False # set True + provide BOT_TOKEN yourself
# SKIP_AUTOJOIN=False # skip joining @TheUltroid on start
# SKIP_ASSISTANT_CUSTOMIZE=False # skip BotFather profile customisation
5 changes: 2 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@
*.session-journal
*.session
build
test*
*.mp3
*.webm
*.webp
*.mp4
*.tgs
*.txt
/*.jpg
/*.png
/*.mp4
Expand All @@ -24,6 +22,7 @@ addons/
vcbot/
__pycache__/
venv/
.venv/
node_modules/
glitch_me/
src/glitch-me
Expand All @@ -44,4 +43,4 @@ bin-release/
*.raw

# fly.io configs
fly.toml
fly.toml
18 changes: 14 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,13 @@ Get the [Necessary Variables](#Necessary-Variables) and then click the button be
- Create a virtual env:
`virtualenv -p /usr/bin/python3 venv`
`. ./venv/bin/activate`
- Install the requirements:
`pip(3) install -U -r re*/st*/optional-requirements.txt`
`pip(3) install -U -r requirements.txt`
- **Recommended:** guided setup (writes `.env`, installs deps by profile):
`python -m pyUltroid setup`
Health check anytime: `python -m pyUltroid doctor`
- Or install requirements manually:
`pip install -U -r requirements.txt`
`pip install -U -r requirements-db-redis.txt` # or requirements-db-mongo.txt / requirements-db-postgres.txt
`pip install -U -r requirements-full.txt` # optional plugin extras
- Generate your `SESSION`:
- For Linux users:
`bash sessiongen`
Expand All @@ -91,17 +95,23 @@ Get the [Necessary Variables](#Necessary-Variables) and then click the button be

---
## Necessary Variables
- `API_ID` / `API_HASH` - from [my.telegram.org](https://my.telegram.org) (**required**; shared Telegram Android defaults are rejected).
- `SESSION` - SessionString for your accounts login session. Get it from [here](#Session-String)

One of the following database:
- For **Redis** (tutorial [here](./resources/extras/redistut.md))
- `REDIS_URI` - Redis endpoint URL, from [redislabs](http://redislabs.com/).
- `REDIS_URI` - Redis `host:port` only (no `redis://` / `https://`), from [redislabs](http://redislabs.com/) or similar.
- `REDIS_PASSWORD` - Redis endpoint Password, from [redislabs](http://redislabs.com/).
- For **MONGODB**
- `MONGO_URI` - Get it from [mongodb](https://mongodb.com/atlas).
- For **SQLDB**
- `DATABASE_URL`- Get it from [elephantsql](https://elephantsql.com).

Optional safety toggles (see `.env.sample`):
- `ULTROID_AUTO_PIP` — runtime pip fallback for missing DB drivers (`1` default, set `0` on locked hosts).
- `SKIP_AUTOPILOT` / `SKIP_AUTOBOT` / `SKIP_AUTOJOIN` / `SKIP_ASSISTANT_CUSTOMIZE` — opt out of startup side-effects.
- `ULTROID_STRICT_CONFIG=1` — require a remote DB at boot.

## Session String
Different ways to get your `SESSION`:
* [![Run on Repl.it](https://replit.com/badge/github/TeamUltroid/Ultroid)](https://replit.com/@TeamUltroid/UltroidStringSession)
Expand Down
68 changes: 58 additions & 10 deletions app.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +15,49 @@
"stack": "container",
"env": {
"API_ID": {
"description": "You api id, from my.telegram.org or @ScrapperRoBot.",
"value": "",
"required": false
"description": "Your api id from https://my.telegram.org (required — no shared defaults).",
"value": "",
"required": true
},
"API_HASH": {
"description": "You api hash, from my.telegram.org or @ScrapperRoBot.",
"description": "Your api hash from https://my.telegram.org (required — no shared defaults).",
"value": "",
"required": false
"required": true
},
"SESSION": {
"description": "Session String (telethon or pyrogram) for your telegram user account. The userbot will NOT work without a session string!!",
"value": ""
"value": "",
"required": true
},
"REDIS_URI": {
"description": "Redis endpoint URL, from redislabs.com",
"value": ""
"description": "Redis host:port only (no redis://). Example: redis-123.example.com:12345",
"value": "",
"required": false
},
"REDIS_PASSWORD": {
"description": "Redis endpoint password, from redislabs.com",
"value": ""
"description": "Redis password from your provider.",
"value": "",
"required": false
},
"MONGO_URI": {
"description": "Optional MongoDB URI (mongodb:// or mongodb+srv://). Use instead of Redis.",
"value": "",
"required": false
},
"DATABASE_URL": {
"description": "Optional Postgres URI (postgres://...). Use instead of Redis/Mongo.",
"value": "",
"required": false
},
"BOT_TOKEN": {
"description": "Optional assistant bot token from @BotFather. Autopilot can create one if omitted.",
"value": "",
"required": false
},
"LOG_CHANNEL": {
"description": "Optional log channel/group id. Autopilot can create one if omitted.",
"value": "",
"required": false
},
"HEROKU_API": {
"description": "Heroku API token. Mandatory for Heroku Deploy...",
Expand All @@ -45,6 +68,31 @@
"description": "Name of your Heroku app, given in the first blank on this page. To be added if deploying to heroku ONLY.",
"value": "",
"required": false
},
"ULTROID_AUTO_PIP": {
"description": "Allow runtime pip install of missing DB drivers (1/0). Default 1 for back-compat; set 0 on locked hosts.",
"value": "1",
"required": false
},
"SKIP_AUTOJOIN": {
"description": "Set True to skip auto-joining @TheUltroid on startup.",
"value": "False",
"required": false
},
"SKIP_AUTOBOT": {
"description": "Set True to skip BotFather bot creation (requires BOT_TOKEN).",
"value": "False",
"required": false
},
"SKIP_AUTOPILOT": {
"description": "Set True to skip auto log-channel creation (requires LOG_CHANNEL).",
"value": "False",
"required": false
},
"SKIP_ASSISTANT_CUSTOMIZE": {
"description": "Set True to skip assistant profile customisation at @BotFather.",
"value": "False",
"required": false
}
},
"formation": {
Expand Down
125 changes: 123 additions & 2 deletions assistant/initial.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,52 @@
}


def _onboard_text():
lang = udB.get_key("language") or "en"
hndlr = udB.get_key("HNDLR") or "."
pmpermit = "on" if udB.get_key("PMLOG") or udB.get_key("PMREQUEST") else "default"
addons = "on" if udB.get_key("ADDONS") else "off"
autojoin = "skipped" if udB.get_key("SKIP_AUTOJOIN") else "allowed"
return f"""⚙️ **Quick setup**

Current values (tap to toggle / cycle):

• **Language**: `{lang}`
• **Handler (HNDLR)**: `{hndlr}`
• **Addons**: `{addons}`
• **Auto-join @TheUltroid**: `{autojoin}`

Use `{hndlr}setdb KEY value` for advanced keys.
When finished, tap **Done**."""


def _onboard_buttons():
return [
[
Button.inline("Language", "onboard_lang"),
Button.inline("Handler", "onboard_hndlr"),
],
[
Button.inline("Toggle Addons", "onboard_addons"),
Button.inline("Toggle Auto-join", "onboard_join"),
],
[
Button.inline("PM Permit tip", "onboard_pm"),
Button.inline("Done ✓", "onboard_done"),
],
]


@callback(re.compile("initft_(\\d+)"))
async def init_depl(e):
CURRENT = int(e.data_match.group(1))
if CURRENT == 5:
return await e.edit(
STRINGS[5],
buttons=Button.inline("<< Back", "initbk_4"),
buttons=[
Button.inline("<< Back", "initbk_4"),
Button.inline("⚙️ Quick setup", "onboard_menu"),
],
link_preview=False,
)

Expand All @@ -70,7 +109,10 @@ async def ineiq(e):
if CURRENT == 1:
return await e.edit(
STRINGS[1],
buttons=Button.inline("Start Back >>", "initft_2"),
buttons=[
Button.inline("Start Back >>", "initft_2"),
Button.inline("⚙️ Quick setup", "onboard_menu"),
],
link_preview=False,
)

Expand All @@ -82,3 +124,82 @@ async def ineiq(e):
],
link_preview=False,
)


@callback("onboard_menu")
async def onboard_menu(e):
await e.edit(_onboard_text(), buttons=_onboard_buttons(), link_preview=False)


@callback("onboard_lang")
async def onboard_lang(e):
# Cycle a small set of common languages present under strings/
langs = ["en", "hi", "ar", "id", "pt", "es", "ru", "tr"]
cur = udB.get_key("language") or "en"
try:
nxt = langs[(langs.index(cur) + 1) % len(langs)]
except ValueError:
nxt = "en"
udB.set_key("language", nxt)
await e.answer(f"Language → {nxt}")
await e.edit(_onboard_text(), buttons=_onboard_buttons(), link_preview=False)


@callback("onboard_hndlr")
async def onboard_hndlr(e):
options = [".", "!", "/", "?", "$"]
cur = udB.get_key("HNDLR") or "."
try:
nxt = options[(options.index(cur) + 1) % len(options)]
except ValueError:
nxt = "."
udB.set_key("HNDLR", nxt)
await e.answer(f"HNDLR → {nxt} (restart recommended)")
await e.edit(_onboard_text(), buttons=_onboard_buttons(), link_preview=False)


@callback("onboard_addons")
async def onboard_addons(e):
cur = bool(udB.get_key("ADDONS"))
udB.set_key("ADDONS", not cur)
await e.answer("Addons " + ("on" if not cur else "off") + " (restart to apply)")
await e.edit(_onboard_text(), buttons=_onboard_buttons(), link_preview=False)


@callback("onboard_join")
async def onboard_join(e):
cur = bool(udB.get_key("SKIP_AUTOJOIN"))
udB.set_key("SKIP_AUTOJOIN", not cur)
await e.answer("Auto-join " + ("skipped" if not cur else "allowed"))
await e.edit(_onboard_text(), buttons=_onboard_buttons(), link_preview=False)


@callback("onboard_pm")
async def onboard_pm(e):
h = udB.get_key("HNDLR") or "."
await e.answer("See tip in message", alert=False)
await e.edit(
f"""🛡 **PM Permit**

Protect your PM with:
• `{h}setdb PMLOG True` — log private messages
• Official plugin: `{h}help pmpermit` (if loaded)

Back to setup when ready.""",
buttons=[[Button.inline("« Back", "onboard_menu")]],
link_preview=False,
)


@callback("onboard_done")
async def onboard_done(e):
udB.set_key("ONBOARD_DONE", "Done")
h = udB.get_key("HNDLR") or "."
await e.edit(
f"""✅ **Setup saved.**

• Some toggles need a restart (`{h}restart`) to fully apply.
• Help: `{h}help` · Health: `python -m pyUltroid doctor` on the host.""",
buttons=[[Button.inline("« Intro", "initft_1")]],
link_preview=False,
)
Loading
Loading