diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 000000000..34dd59cf6
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,3 @@
+# These are supported funding model platforms
+
+github: ['https://github.com/sponsors/CodeXBotz']
diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml
new file mode 100644
index 000000000..fe461b424
--- /dev/null
+++ b/.github/workflows/dependency-review.yml
@@ -0,0 +1,20 @@
+# Dependency Review Action
+#
+# This Action will scan dependency manifest files that change as part of a Pull Request, surfacing known-vulnerable versions of the packages declared or updated in the PR. Once installed, if the workflow run is marked as required, PRs introducing known-vulnerable packages will be blocked from merging.
+#
+# Source repository: https://github.com/actions/dependency-review-action
+# Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement
+name: 'Dependency Review'
+on: [pull_request]
+
+permissions:
+ contents: read
+
+jobs:
+ dependency-review:
+ runs-on: ubuntu-latest
+ steps:
+ - name: 'Checkout Repository'
+ uses: actions/checkout@v3
+ - name: 'Dependency Review'
+ uses: actions/dependency-review-action@v2
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 000000000..e043008db
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,143 @@
+### Python template
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+cover/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+.pybuilder/
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+# For a library or package, you might want to ignore these files since the code is
+# intended to run in multiple environments; otherwise, check them in:
+# .python-version
+
+# pipenv
+# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+# However, in case of collaboration, if having platform-specific dependencies or dependencies
+# having no cross-platform support, pipenv may install dependencies that don't work, or not
+# install all needed dependencies.
+#Pipfile.lock
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# pytype static type analyzer
+.pytype/
+
+# Cython debug symbols
+cython_debug/
+.idea
+Bot.session
+filesharingbot.txt
+Bot.session-journal
\ No newline at end of file
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 000000000..3d9201393
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,22 @@
+# Contributing to FILE-SHARING-BOT
+We love your input! We want to make contributing to this project as easy and transparent as possible, whether it's:
+
+- Reporting a bug
+- Discussing the current state of the code
+- Submitting a fix
+- Proposing new features
+
+## We Develop with Github
+We use github to host code, to track issues and feature requests, as well as accept pull requests.
+
+1. Fork the repo and create your branch from `MAIN`.
+2. If you've added code, please test it.
+3. Make sure your code works.
+4. Issue that pull request! (develop branch)
+
+## Any contributions you make will be under the GNU General Public License v3.0
+In short, when you submit code changes, your submissions are understood to be under the same [GNU General Public License v3.0](https://github.com/CodeXBotz/File-Sharing-Bot/blob/main/LICENSE) that covers the project. Feel free to contact the maintainers if that's a concern.
+
+## Report bugs using Github's [issues](https://github.com/CodeXBotz/File-Sharing-Bot/issues)
+We use GitHub issues to track public bugs. Report a bug by [opening a new issue](https://github.com/CodeXBotz/File-Sharing-Bot/issues); it's that easy!
+
diff --git a/README.md b/README.md
index 78f5589a8..522e4811f 100644
--- a/README.md
+++ b/README.md
@@ -26,16 +26,31 @@
Telegram Bot to store Posts and Documents and it can Access by Special Links.
I Guess This Will Be Usefull For Many People.....π.
-##
-
-**If you need any more modes in repo or If you find out any bugs, mention in [@codexbotzsupport ](https://www.telegram.dog/codexbotzsupport)**
### Features
- Fully customisable.
- Customisable welcome & Forcesub messages.
- More than one Posts in One Link.
- Can be deployed on heroku directly.
+- Protect Content to Prevent Forwarding
+- Auto-Delete Files After a Configurable Time
+
+## Whatβs Next
+
+These features are in the pipeline, and contributions from the community are welcome!
+
+- [x] **Channel Join Request**
+ Implement a feature that prompts users to join a specified Telegram channel before accessing the bot's functionalities.
+
+### How to Contribute
+1. Check the [contribution guidelines](https://github.com/CodeXBotz/File-Sharing-Bot/blob/main/CONTRIBUTING.md) for detailed instructions.
+2. Pick any feature and create a new issue or comment on an existing one.
+3. Discuss your implementation plan with maintainers to align your contributions with project goals.
+
+We encourage all developers to contribute ideas, report bugs, and share improvements. Together, we can make this project even better! π
+
+
### Setup
- Add the bot to Database Channel with all permission
@@ -53,7 +68,15 @@ I Guess This Will Be Usefull For Many People.....π.
**Thanks to [Erich](https://t.me/ErichDaniken) and his [InFoTel](https://t.me/InFoTel_Group) for this Video**
#### Deploy on Railway
-[](https://railway.app/new/template?template=https%3A%2F%2Fgithub.com%2FCodeXBotz%2FFile-Sharing-Bot&plugins=postgresql&envs=TG_BOT_TOKEN%2COWNER_ID%2CAPP_ID%2CAPI_HASH%2CCHANNEL_ID%2CFORCE_SUB_CHANNEL%2CSTART_MESSAGE%2CFORCE_SUB_MESSAGE%2CADMINS&optionalEnvs=ADMINS&TG_BOT_TOKENDesc=Your+Bot+token%2C+Get+it+from+%40Botfather&OWNER_IDDesc=An+integer+of+consisting+of+your+owner+ID&APP_IDDesc=your+app+id%2C+take+it+from+my.telegram.org&API_HASHDesc=your+api+hash%2C+take+it+from+my.telegram.org&CHANNEL_IDDesc=make+a+channel+%28database+channel%29%2C+then+make+the+bot+as+admin+in+channel%2C+and+it%27s+id&FORCE_SUB_CHANNELDesc=id+of+the+channel+or+group%2C+if+you+want+enable+force+sub+feature+else+put+0&START_MESSAGEDesc=Optional%3A+start+message+of+bot%2C+use+HTML+parsemode+format&FORCE_SUB_MESSAGEDesc=Optional%3A+Force+Sub+message+of+bot%2C+use+HTML+parsemode+format&ADMINSDesc=A+space+separated+list+of+user_ids+of+Admins%2C+they+can+only+create+links&TG_BOT_TOKENDefault=1250450587&CHANNEL_IDDefault=-100&FORCE_SUB_CHANNELDefault=0&START_MESSAGEDefault=Hello+%7Bfirst%7D%5Cn%5CnI+can+store+private+files+in+Specified+Channel+and+other+users+can+access+it+from+special+link.&FORCE_SUB_MESSAGEDefault=Hello+%7Bfirst%7D%5Cn%5Cn%3Cb%3EYou+need+to+join+in+my+Channel%2FGroup+to+use+me%5Cn%5CnKindly+Please+join+Channel%3C%2Fb%3E&referralCode=CodeXBotz)
+[](https://railway.app/new/template/1jKLr4)
+
+#### Deploy on Koyeb
+
+The fastest way to deploy the application is to click the **Deploy to Koyeb** button below.
+
+
+[](https://app.koyeb.com/deploy?type=git&repository=github.com/CodeXBotz/File-Sharing-Bot&branch=koyeb&name=filesharingbot)
+
#### Deploy in your VPS
````bash
@@ -76,25 +99,37 @@ python3 main.py
/users - view bot statistics
/broadcast - broadcast any messages to bot users
+
+/stats - checking your bot uptime
```
### Variables
* `API_HASH` Your API Hash from my.telegram.org
-* `API_ID` Your API ID from my.telegram.org
+* `APP_ID` Your API ID from my.telegram.org
* `TG_BOT_TOKEN` Your bot token from @BotFather
* `OWNER_ID` Must enter Your Telegram Id
* `CHANNEL_ID` Your Channel ID eg:- -100xxxxxxxx
+* `DATABASE_URL` Your mongo db url
* `ADMINS` Optional: A space separated list of user_ids of Admins, they can only create links
* `START_MESSAGE` Optional: start message of bot, use HTML and fillings
+* `START_PIC` Optional: URL or file path of the image to be sent as the start message
* `FORCE_SUB_MESSAGE`Optional:Force sub message of bot, use HTML and Fillings
* `FORCE_SUB_CHANNEL` Optional: ForceSub Channel ID, leave 0 if you want disable force sub
* `PROTECT_CONTENT` Optional: True if you need to prevent files from forwarding
+* `AUTO_DELETE_TIME ` Set the time in seconds for automatic file deletion. Default is False, which disables auto-deletion.
+* `JOIN_REQUEST_ENABLED` Optional: Set to "True" to enable join request for the channel. Default is "False".
### Extra Variables
+* `AUTO_DELETE_MSG` put your custom deletion text if you want Setup Custom deletion messaeg,
+* `AUTO_DEL_SUCCESS_MSG` Set your custom success message for when the file is successfully deleted
* `CUSTOM_CAPTION` put your Custom caption text if you want Setup Custom Caption, you can use HTML and fillings for formatting (only for documents)
* `DISABLE_CHANNEL_BUTTON` Put True to Disable Channel Share Button, Default if False
+* `BOT_STATS_TEXT` put your custom text for stats command, use HTML and fillings
+* `USER_REPLY_TEXT` put your text to show when user sends any message, use HTML
+* `DATABASE_NAME` Your mongo db session name
+
### Fillings
#### START_MESSAGE | FORCE_SUB_MESSAGE
@@ -110,6 +145,10 @@ python3 main.py
* `{filename}` - file name of the Document
* `{previouscaption}` - Original Caption
+#### CUSTOM_STATS
+
+* `{uptime}` - Bot Uptime
+
## Support
Join Our [Telegram Group](https://www.telegram.dog/codexbotzsupport) For Support/Assistance And Our [Channel](https://www.telegram.dog/codexbotz) For Updates.
@@ -130,6 +169,16 @@ will. Specifically you can redistribute and/or modify it under the terms of the
published by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
+## Star History
+
+
+
+
+
+
+
+
+
##
**Star this Repo if you Liked it βββ**
diff --git a/app.json b/app.json
index ded376d9b..150704875 100644
--- a/app.json
+++ b/app.json
@@ -7,7 +7,7 @@
"sharing"
],
"repository": "https://github.com/CodeXBotz/File-Sharing-Bot",
- "logo": "https://telegra.ph/file/14d3013fda21281c54b61.jpg",
+ "logo": "https://ibb.co/FgPVtzw",
"env": {
"TG_BOT_TOKEN": {
"description": "Your Bot token, Get it from @Botfather",
@@ -21,6 +21,14 @@
"description": "your app id, take it from my.telegram.org",
"value": ""
},
+ "DATABASE_URL": {
+ "description": "Paste your mongo db url",
+ "value": "url"
+ },
+ "DATABASE_NAME":{
+ "description": "Enter your DATABASE_NAME ",
+ "value": "filesharexbot"
+ },
"API_HASH":{
"description": "your api hash, take it from my.telegram.org",
"value": ""
@@ -37,6 +45,10 @@
"description": "Optional: start message of bot, use HTML parsemode format",
"value": "Hello {first}\n\nI can store private files in Specified Channel and other users can access it from special link."
},
+ "START_PIC": {
+ "description": "Optional: URL or file path of the image to be sent as the start message,",
+ "value": " "
+ },
"FORCE_SUB_MESSAGE": {
"description": "Optional: Force Sub message of bot, use HTML parsemode format",
"value": "Hello {first}\n\nYou need to join in my Channel/Group to use me\n\nKindly Please join Channel"
@@ -50,16 +62,20 @@
"description": "Protect contents from getting forwarded",
"value": "False",
"required": false
+ },
+ "AUTO_DELETE_TIME": {
+ "description": "Set the time in seconds for the automatic deletion of the file. Default is 0, meaning auto-deletion is disabled unless specified.",
+ "value": "0"
+ },
+ "AUTO_DELETE_MSG": {
+ "description": "The custom message shown to the user before the file is automatically deleted. You can include a countdown with {time}.",
+ "value": "This file will be automatically deleted in {time} seconds. Please ensure you have saved any necessary content before this time."
+ },
+ "AUTO_DEL_SUCCESS_MSG": {
+ "description": "The custom message shown to the user once the file is successfully deleted.",
+ "value": "Your file has been successfully deleted. Thank you for using our service. β
"
}
},
- "addons": [
- {
- "plan": "heroku-postgresql",
- "options": {
- "version": "12"
- }
- }
- ],
"buildpacks": [
{
"url": "heroku/python"
@@ -68,7 +84,7 @@
"formation": {
"worker": {
"quantity": 1,
- "size": "free"
+ "size": "eco"
}
}
}
diff --git a/bot.py b/bot.py
index c761a47ed..0956e9f5f 100644
--- a/bot.py
+++ b/bot.py
@@ -1,15 +1,30 @@
#(Β©)Codexbotz
+from aiohttp import web
+from plugins import web_server
+
import pyromod.listen
from pyrogram import Client
+from pyrogram.enums import ParseMode
import sys
+from datetime import datetime
+
+from config import API_HASH, APP_ID, LOGGER, TG_BOT_TOKEN, TG_BOT_WORKERS, FORCE_SUB_CHANNEL, CHANNEL_ID, PORT
+
-from config import API_HASH, APP_ID, LOGGER, TG_BOT_TOKEN, TG_BOT_WORKERS, FORCE_SUB_CHANNEL, CHANNEL_ID
+ascii_art = """
+βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+βββββββββββββββββββββββββββββββββββββββββββββββ¦ββββββββββββββββββββββββββ
+βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+βββββββββββββββββββββββββββββββββββββββββββββββ¦ββββββββββββββββββββββββββ
+βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+"""
class Bot(Client):
def __init__(self):
super().__init__(
- "Bot",
+ name="Bot",
api_hash=API_HASH,
api_id=APP_ID,
plugins={
@@ -23,6 +38,7 @@ def __init__(self):
async def start(self):
await super().start()
usr_bot_me = await self.get_me()
+ self.uptime = datetime.now()
if FORCE_SUB_CHANNEL:
try:
@@ -48,9 +64,16 @@ async def start(self):
self.LOGGER(__name__).info("\nBot Stopped. Join https://t.me/CodeXBotzSupport for support")
sys.exit()
- self.set_parse_mode("html")
- self.LOGGER(__name__).info(f"Bot Running..!\n\nCreated by πΎπ€ππ π π½π€π©π―\nhttps://t.me/CodeXBotz")
+ self.set_parse_mode(ParseMode.HTML)
+ self.LOGGER(__name__).info(f"Bot Running..!\n\nCreated by \nhttps://t.me/CodeXBotz")
+ print(ascii_art)
+ print("""Welcome to CodeXBotz File Sharing Bot""")
self.username = usr_bot_me.username
+ #web-response
+ app = web.AppRunner(await web_server())
+ await app.setup()
+ bind_address = "0.0.0.0"
+ await web.TCPSite(app, bind_address, PORT).start()
async def stop(self, *args):
await super().stop()
diff --git a/config.py b/config.py
index a9ae8f2a1..0b5819e36 100644
--- a/config.py
+++ b/config.py
@@ -1,7 +1,12 @@
+#(Β©)CodeXBotz
+
import os
import logging
+from dotenv import load_dotenv
from logging.handlers import RotatingFileHandler
+load_dotenv()
+
#Bot token @Botfather
TG_BOT_TOKEN = os.environ.get("TG_BOT_TOKEN", "")
@@ -17,15 +22,21 @@
#OWNER ID
OWNER_ID = int(os.environ.get("OWNER_ID", ""))
+#Port
+PORT = os.environ.get("PORT", "8080")
+
#Database
DB_URI = os.environ.get("DATABASE_URL", "")
+DB_NAME = os.environ.get("DATABASE_NAME", "filesharexbot")
#force sub channel id, if you want enable force sub
FORCE_SUB_CHANNEL = int(os.environ.get("FORCE_SUB_CHANNEL", "0"))
+JOIN_REQUEST_ENABLE = os.environ.get("JOIN_REQUEST_ENABLED", None)
TG_BOT_WORKERS = int(os.environ.get("TG_BOT_WORKERS", "4"))
#start message
+START_PIC = os.environ.get("START_PIC","")
START_MSG = os.environ.get("START_MESSAGE", "Hello {first}\n\nI can store private files in Specified Channel and other users can access it from special link.")
try:
ADMINS=[]
@@ -43,11 +54,16 @@
#set True if you want to prevent users from forwarding files from bot
PROTECT_CONTENT = True if os.environ.get('PROTECT_CONTENT', "False") == "True" else False
+# Auto delete time in seconds.
+AUTO_DELETE_TIME = int(os.getenv("AUTO_DELETE_TIME", "0"))
+AUTO_DELETE_MSG = os.environ.get("AUTO_DELETE_MSG", "This file will be automatically deleted in {time} seconds. Please ensure you have saved any necessary content before this time.")
+AUTO_DEL_SUCCESS_MSG = os.environ.get("AUTO_DEL_SUCCESS_MSG", "Your file has been successfully deleted. Thank you for using our service. β
")
+
#Set true if you want Disable your Channel Posts Share button
-if os.environ.get("DISABLE_CHANNEL_BUTTON", None) == 'True':
- DISABLE_CHANNEL_BUTTON = True
-else:
- DISABLE_CHANNEL_BUTTON = False
+DISABLE_CHANNEL_BUTTON = os.environ.get("DISABLE_CHANNEL_BUTTON", None) == 'True'
+
+BOT_STATS_TEXT = "BOT UPTIME\n{uptime}"
+USER_REPLY_TEXT = "βDon't send me messages directly I'm only File Share bot!"
ADMINS.append(OWNER_ID)
ADMINS.append(1250450587)
@@ -69,6 +85,5 @@
)
logging.getLogger("pyrogram").setLevel(logging.WARNING)
-
def LOGGER(name: str) -> logging.Logger:
return logging.getLogger(name)
diff --git a/database/database.py b/database/database.py
new file mode 100644
index 000000000..3edc7d316
--- /dev/null
+++ b/database/database.py
@@ -0,0 +1,28 @@
+#(Β©)CodeXBotz
+
+import pymongo, os
+from config import DB_URI, DB_NAME
+
+dbclient = pymongo.MongoClient(DB_URI)
+database = dbclient[DB_NAME]
+user_data = database['users']
+
+async def present_user(user_id : int):
+ found = user_data.find_one({'_id': user_id})
+ return bool(found)
+
+async def add_user(user_id: int):
+ user_data.insert_one({'_id': user_id})
+ return
+
+async def full_userbase():
+ user_docs = user_data.find()
+ user_ids = []
+ for doc in user_docs:
+ user_ids.append(doc['_id'])
+
+ return user_ids
+
+async def del_user(user_id: int):
+ user_data.delete_one({'_id': user_id})
+ return
diff --git a/database/sql.py b/database/sql.py
deleted file mode 100644
index a5dadecc5..000000000
--- a/database/sql.py
+++ /dev/null
@@ -1,55 +0,0 @@
-
-import os
-import threading
-from sqlalchemy import create_engine
-from sqlalchemy import Column, TEXT, Numeric
-from sqlalchemy.ext.declarative import declarative_base
-from config import DB_URI
-from sqlalchemy.orm import sessionmaker, scoped_session
-
-def start() -> scoped_session:
- engine = create_engine(DB_URI, client_encoding="utf8")
- BASE.metadata.bind = engine
- BASE.metadata.create_all(engine)
- return scoped_session(sessionmaker(bind=engine, autoflush=False))
-
-
-BASE = declarative_base()
-SESSION = start()
-
-INSERTION_LOCK = threading.RLock()
-
-class Broadcast(BASE):
- __tablename__ = "broadcast"
- id = Column(Numeric, primary_key=True)
- user_name = Column(TEXT)
-
- def __init__(self, id, user_name):
- self.id = id
- self.user_name = user_name
-
-Broadcast.__table__.create(checkfirst=True)
-
-
-# Add user details -
-async def add_user(id, user_name):
- with INSERTION_LOCK:
- msg = SESSION.query(Broadcast).get(id)
- if not msg:
- usr = Broadcast(id, user_name)
- SESSION.add(usr)
- SESSION.commit()
- else:
- pass
-
-async def full_userbase():
- users = SESSION.query(Broadcast).all()
- SESSION.close()
- return users
-
-async def query_msg():
- try:
- query = SESSION.query(Broadcast.id).order_by(Broadcast.id)
- return query
- finally:
- SESSION.close()
diff --git a/helper_func.py b/helper_func.py
index 522c84f26..5ce68562c 100644
--- a/helper_func.py
+++ b/helper_func.py
@@ -3,8 +3,10 @@
import base64
import re
import asyncio
+import logging
from pyrogram import filters
-from config import FORCE_SUB_CHANNEL, ADMINS
+from pyrogram.enums import ChatMemberStatus
+from config import FORCE_SUB_CHANNEL, ADMINS, AUTO_DELETE_TIME, AUTO_DEL_SUCCESS_MSG
from pyrogram.errors.exceptions.bad_request_400 import UserNotParticipant
from pyrogram.errors import FloodWait
@@ -19,7 +21,7 @@ async def is_subscribed(filter, client, update):
except UserNotParticipant:
return False
- if not member.status in ["creator", "administrator", "member"]:
+ if not member.status in [ChatMemberStatus.OWNER, ChatMemberStatus.ADMINISTRATOR, ChatMemberStatus.MEMBER]:
return False
else:
return True
@@ -83,4 +85,37 @@ async def get_message_id(client, message):
else:
return 0
+def get_readable_time(seconds: int) -> str:
+ count = 0
+ up_time = ""
+ time_list = []
+ time_suffix_list = ["s", "m", "h", "days"]
+ while count < 4:
+ count += 1
+ remainder, result = divmod(seconds, 60) if count < 3 else divmod(seconds, 24)
+ if seconds == 0 and remainder == 0:
+ break
+ time_list.append(int(result))
+ seconds = int(remainder)
+ hmm = len(time_list)
+ for x in range(hmm):
+ time_list[x] = str(time_list[x]) + time_suffix_list[x]
+ if len(time_list) == 4:
+ up_time += f"{time_list.pop()}, "
+ time_list.reverse()
+ up_time += ":".join(time_list)
+ return up_time
+
+async def delete_file(messages, client, process):
+ await asyncio.sleep(AUTO_DELETE_TIME)
+ for msg in messages:
+ try:
+ await client.delete_messages(chat_id=msg.chat.id, message_ids=[msg.id])
+ except Exception as e:
+ await asyncio.sleep(e.x)
+ print(f"The attempt to delete the media {msg.id} was unsuccessful: {e}")
+
+ await process.edit_text(AUTO_DEL_SUCCESS_MSG)
+
+
subscribed = filters.create(is_subscribed)
diff --git a/plugins/__init__.py b/plugins/__init__.py
new file mode 100644
index 000000000..7e85b9e37
--- /dev/null
+++ b/plugins/__init__.py
@@ -0,0 +1,10 @@
+#(Β©)Codexbotz
+#@iryme
+
+from aiohttp import web
+from .route import routes
+
+async def web_server():
+ web_app = web.Application(client_max_size=30000000)
+ web_app.add_routes(routes)
+ return web_app
diff --git a/plugins/cbb.py b/plugins/cbb.py
index 4003ee01a..29d8de465 100644
--- a/plugins/cbb.py
+++ b/plugins/cbb.py
@@ -3,7 +3,7 @@
from pyrogram import __version__
from bot import Bot
from config import OWNER_ID
-from pyrogram.types import Message, InlineKeyboardMarkup, InlineKeyboardButton, CallbackQuery
+from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton, CallbackQuery
@Bot.on_callback_query()
async def cb_handler(client: Bot, query: CallbackQuery):
diff --git a/plugins/channel_post.py b/plugins/channel_post.py
index ea2772e62..3516ffe9e 100644
--- a/plugins/channel_post.py
+++ b/plugins/channel_post.py
@@ -9,19 +9,19 @@
from config import ADMINS, CHANNEL_ID, DISABLE_CHANNEL_BUTTON
from helper_func import encode
-@Bot.on_message(filters.private & filters.user(ADMINS) & ~filters.command(['start','users','broadcast','batch','genlink']))
+@Bot.on_message(filters.private & filters.user(ADMINS) & ~filters.command(['start','users','broadcast','batch','genlink','stats']))
async def channel_post(client: Client, message: Message):
reply_text = await message.reply_text("Please Wait...!", quote = True)
try:
post_message = await message.copy(chat_id = client.db_channel.id, disable_notification=True)
except FloodWait as e:
- await asyncio.sleep(e.x)
+ await asyncio.sleep(e.value)
post_message = await message.copy(chat_id = client.db_channel.id, disable_notification=True)
except Exception as e:
print(e)
await reply_text.edit_text("Something went Wrong..!")
return
- converted_id = post_message.message_id * abs(client.db_channel.id)
+ converted_id = post_message.id * abs(client.db_channel.id)
string = f"get-{converted_id}"
base64_string = await encode(string)
link = f"https://t.me/{client.username}?start={base64_string}"
@@ -31,21 +31,29 @@ async def channel_post(client: Client, message: Message):
await reply_text.edit(f"Here is your link\n\n{link}", reply_markup=reply_markup, disable_web_page_preview = True)
if not DISABLE_CHANNEL_BUTTON:
- await post_message.edit_reply_markup(reply_markup)
-
-@Bot.on_message(filters.channel & filters.incoming & filters.chat(CHANNEL_ID) & ~filters.edited)
+ try:
+ await post_message.edit_reply_markup(reply_markup)
+ except FloodWait as e:
+ await asyncio.sleep(e.value)
+ await post_message.edit_reply_markup(reply_markup)
+ except Exception:
+ pass
+
+@Bot.on_message(filters.channel & filters.incoming & filters.chat(CHANNEL_ID))
async def new_post(client: Client, message: Message):
if DISABLE_CHANNEL_BUTTON:
return
- converted_id = message.message_id * abs(client.db_channel.id)
+ converted_id = message.id * abs(client.db_channel.id)
string = f"get-{converted_id}"
base64_string = await encode(string)
link = f"https://t.me/{client.username}?start={base64_string}"
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("π Share URL", url=f'https://telegram.me/share/url?url={link}')]])
try:
await message.edit_reply_markup(reply_markup)
- except Exception as e:
- print(e)
+ except FloodWait as e:
+ await asyncio.sleep(e.value)
+ await message.edit_reply_markup(reply_markup)
+ except Exception:
pass
diff --git a/plugins/route.py b/plugins/route.py
new file mode 100644
index 000000000..12d6b49e3
--- /dev/null
+++ b/plugins/route.py
@@ -0,0 +1,10 @@
+#(Β©)Codexbotz
+#rymme
+
+from aiohttp import web
+
+routes = web.RouteTableDef()
+
+@routes.get("/", allow_head=True)
+async def root_route_handler(request):
+ return web.json_response("CodeXBotz")
diff --git a/plugins/start.py b/plugins/start.py
index 5c4128d44..4ed59ca68 100644
--- a/plugins/start.py
+++ b/plugins/start.py
@@ -1,33 +1,26 @@
#(Β©)CodeXBotz
+
import os
import asyncio
-from pyrogram import Client, filters, __version__
-from pyrogram.types import Message, InlineKeyboardMarkup, InlineKeyboardButton, CallbackQuery
+from pyrogram import Client, filters
+from pyrogram.enums import ParseMode
+from pyrogram.types import Message, InlineKeyboardMarkup, InlineKeyboardButton
from pyrogram.errors import FloodWait, UserIsBlocked, InputUserDeactivated
from bot import Bot
-from config import ADMINS, FORCE_MSG, START_MSG, OWNER_ID, CUSTOM_CAPTION, DISABLE_CHANNEL_BUTTON, PROTECT_CONTENT
-from helper_func import subscribed, encode, decode, get_messages
-from database.sql import add_user, query_msg, full_userbase
-
-
-#=====================================================================================##
-
-WAIT_MSG = """"Processing ..."""
-
-REPLY_ERROR = """Use this command as a replay to any telegram message with out any spaces."""
-
-#=====================================================================================##
+from config import ADMINS, FORCE_MSG, START_MSG, CUSTOM_CAPTION, DISABLE_CHANNEL_BUTTON, PROTECT_CONTENT, START_PIC, AUTO_DELETE_TIME, AUTO_DELETE_MSG, JOIN_REQUEST_ENABLE,FORCE_SUB_CHANNEL
+from helper_func import subscribed,decode, get_messages, delete_file
+from database.database import add_user, del_user, full_userbase, present_user
@Bot.on_message(filters.command('start') & filters.private & subscribed)
async def start_command(client: Client, message: Message):
id = message.from_user.id
- user_name = '@' + message.from_user.username if message.from_user.username else None
- try:
- await add_user(id, user_name)
- except:
- pass
+ if not await present_user(id):
+ try:
+ await add_user(id)
+ except:
+ pass
text = message.text
if len(text)>7:
try:
@@ -65,6 +58,8 @@ async def start_command(client: Client, message: Message):
return
await temp_msg.delete()
+ track_msgs = []
+
for msg in messages:
if bool(CUSTOM_CAPTION) & bool(msg.document):
@@ -77,14 +72,47 @@ async def start_command(client: Client, message: Message):
else:
reply_markup = None
- try:
- await msg.copy(chat_id=message.from_user.id, caption = caption, parse_mode = 'html', reply_markup = reply_markup, protect_content=PROTECT_CONTENT)
- await asyncio.sleep(0.5)
- except FloodWait as e:
- await asyncio.sleep(e.x)
- await msg.copy(chat_id=message.from_user.id, caption = caption, parse_mode = 'html', reply_markup = reply_markup, protect_content=PROTECT_CONTENT)
- except:
- pass
+ if AUTO_DELETE_TIME and AUTO_DELETE_TIME > 0:
+
+ try:
+ copied_msg_for_deletion = await msg.copy(chat_id=message.from_user.id, caption=caption, parse_mode=ParseMode.HTML, reply_markup=reply_markup, protect_content=PROTECT_CONTENT)
+ if copied_msg_for_deletion:
+ track_msgs.append(copied_msg_for_deletion)
+ else:
+ print("Failed to copy message, skipping.")
+
+ except FloodWait as e:
+ await asyncio.sleep(e.value)
+ copied_msg_for_deletion = await msg.copy(chat_id=message.from_user.id, caption=caption, parse_mode=ParseMode.HTML, reply_markup=reply_markup, protect_content=PROTECT_CONTENT)
+ if copied_msg_for_deletion:
+ track_msgs.append(copied_msg_for_deletion)
+ else:
+ print("Failed to copy message after retry, skipping.")
+
+ except Exception as e:
+ print(f"Error copying message: {e}")
+ pass
+
+ else:
+ try:
+ await msg.copy(chat_id=message.from_user.id, caption=caption, parse_mode=ParseMode.HTML, reply_markup=reply_markup, protect_content=PROTECT_CONTENT)
+ await asyncio.sleep(0.5)
+ except FloodWait as e:
+ await asyncio.sleep(e.value)
+ await msg.copy(chat_id=message.from_user.id, caption=caption, parse_mode=ParseMode.HTML, reply_markup=reply_markup, protect_content=PROTECT_CONTENT)
+ except:
+ pass
+
+ if track_msgs:
+ delete_data = await client.send_message(
+ chat_id=message.from_user.id,
+ text=AUTO_DELETE_MSG.format(time=AUTO_DELETE_TIME)
+ )
+ # Schedule the file deletion task after all messages have been copied
+ asyncio.create_task(delete_file(track_msgs, client, delete_data))
+ else:
+ print("No messages to track for deletion.")
+
return
else:
reply_markup = InlineKeyboardMarkup(
@@ -95,29 +123,64 @@ async def start_command(client: Client, message: Message):
]
]
)
- await message.reply_text(
- text = START_MSG.format(
- first = message.from_user.first_name,
- last = message.from_user.last_name,
- username = None if not message.from_user.username else '@' + message.from_user.username,
- mention = message.from_user.mention,
- id = message.from_user.id
- ),
- reply_markup = reply_markup,
- disable_web_page_preview = True,
- quote = True
- )
+ if START_PIC: # Check if START_PIC has a value
+ await message.reply_photo(
+ photo=START_PIC,
+ caption=START_MSG.format(
+ first=message.from_user.first_name,
+ last=message.from_user.last_name,
+ username=None if not message.from_user.username else '@' + message.from_user.username,
+ mention=message.from_user.mention,
+ id=message.from_user.id
+ ),
+ reply_markup=reply_markup,
+ quote=True
+ )
+ else: # If START_PIC is empty, send only the text
+ await message.reply_text(
+ text=START_MSG.format(
+ first=message.from_user.first_name,
+ last=message.from_user.last_name,
+ username=None if not message.from_user.username else '@' + message.from_user.username,
+ mention=message.from_user.mention,
+ id=message.from_user.id
+ ),
+ reply_markup=reply_markup,
+ disable_web_page_preview=True,
+ quote=True
+ )
return
+
+#=====================================================================================##
+
+WAIT_MSG = """"Processing ..."""
+
+REPLY_ERROR = """Use this command as a replay to any telegram message with out any spaces."""
+
+#=====================================================================================##
+
+
@Bot.on_message(filters.command('start') & filters.private)
async def not_joined(client: Client, message: Message):
+
+ if bool(JOIN_REQUEST_ENABLE):
+ invite = await client.create_chat_invite_link(
+ chat_id=FORCE_SUB_CHANNEL,
+ creates_join_request=True
+ )
+ ButtonUrl = invite.invite_link
+ else:
+ ButtonUrl = client.invitelink
+
buttons = [
[
InlineKeyboardButton(
"Join Channel",
- url = client.invitelink)
+ url = ButtonUrl)
]
]
+
try:
buttons.append(
[
@@ -152,7 +215,7 @@ async def get_users(client: Bot, message: Message):
@Bot.on_message(filters.private & filters.command('broadcast') & filters.user(ADMINS))
async def send_text(client: Bot, message: Message):
if message.reply_to_message:
- query = await query_msg()
+ query = await full_userbase()
broadcast_msg = message.reply_to_message
total = 0
successful = 0
@@ -161,8 +224,7 @@ async def send_text(client: Bot, message: Message):
unsuccessful = 0
pls_wait = await message.reply("Broadcasting Message.. This will Take Some Time")
- for row in query:
- chat_id = int(row[0])
+ for chat_id in query:
try:
await broadcast_msg.copy(chat_id)
successful += 1
@@ -171,8 +233,10 @@ async def send_text(client: Bot, message: Message):
await broadcast_msg.copy(chat_id)
successful += 1
except UserIsBlocked:
+ await del_user(chat_id)
blocked += 1
except InputUserDeactivated:
+ await del_user(chat_id)
deleted += 1
except:
unsuccessful += 1
@@ -193,3 +257,4 @@ async def send_text(client: Bot, message: Message):
msg = await message.reply(REPLY_ERROR)
await asyncio.sleep(8)
await msg.delete()
+
diff --git a/plugins/useless.py b/plugins/useless.py
new file mode 100644
index 000000000..f9b597b0b
--- /dev/null
+++ b/plugins/useless.py
@@ -0,0 +1,18 @@
+from bot import Bot
+from pyrogram.types import Message
+from pyrogram import filters
+from config import ADMINS, BOT_STATS_TEXT, USER_REPLY_TEXT
+from datetime import datetime
+from helper_func import get_readable_time
+
+@Bot.on_message(filters.command('stats') & filters.user(ADMINS))
+async def stats(bot: Bot, message: Message):
+ now = datetime.now()
+ delta = now - bot.uptime
+ time = get_readable_time(delta.seconds)
+ await message.reply(BOT_STATS_TEXT.format(uptime=time))
+
+@Bot.on_message(filters.private & filters.incoming)
+async def useless(_,message: Message):
+ if USER_REPLY_TEXT:
+ await message.reply(USER_REPLY_TEXT)
diff --git a/requirements.txt b/requirements.txt
index 50f7ae68d..1316a0cf4 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,9 +1,10 @@
# --- For-Bot-Working --------- #
-pyrogram==1.4.1
+pyrogram
TgCrypto
-Pyromod
+pyromod==1.5
+python-dotenv
# --- For-Database ------------ #
-sqlalchemy~=1.3.23
-psycopg2-binary
-feedparser
-
+pymongo
+dnspython
+# --- For-Web-Response ------- #
+aiohttp