├── .gitignore ├── .vscode └── settings.json ├── .github └── workflows │ └── updater.yml ├── ReadMe.md └── main.py /.gitignore: -------------------------------------------------------------------------------- 1 | .env -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "[python]": { 3 | "editor.defaultFormatter": "ms-python.black-formatter" 4 | }, 5 | "python.formatting.provider": "none" 6 | } 7 | -------------------------------------------------------------------------------- /.github/workflows/updater.yml: -------------------------------------------------------------------------------- 1 | name: ⚡️ Update Status. ⚡️ 2 | 3 | on: 4 | schedule: 5 | - cron: "0 */2 * * *" 6 | workflow_dispatch: 7 | 8 | jobs: 9 | update: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: 🐍 Set up Python 3.9 🐍 13 | uses: actions/setup-python@v2 14 | with: 15 | python-version: 3.9 16 | - name: 🌀 Clone the Repo 🌀 17 | uses: actions/checkout@master 18 | with: 19 | repository: ${{ secrets.REPO_NAME }} 20 | - name: 📀 Install dependencies. 📀 21 | run: | 22 | pip install python-decouple pytz telethon==1.37.0 23 | pip install --upgrade pip 24 | - name: 🏃‍♂️ Run the UserClient. 🏃‍♂️ 25 | run: | 26 | python3 main.py 27 | env: 28 | APP_ID: ${{ secrets.APP_ID }} 29 | API_HASH: ${{ secrets.API_HASH }} 30 | BOTS: ${{ secrets.BOTS }} 31 | SESSION: ${{ secrets.SESSION }} 32 | CHANNEL_ID: ${{ secrets.CHANNEL_ID }} 33 | MESSAGE_ID: ${{ secrets.MESSAGE_ID }} 34 | -------------------------------------------------------------------------------- /ReadMe.md: -------------------------------------------------------------------------------- 1 | # BotStatus 2 | Updates your bot status in the message, every two hours. 3 | 4 | **_NOTE:_** This branch uses github workflows to host your code, and doesn't rely on another hosting platform. If you want to deploy on heroku/vps/whereever, go to [this branch](https://github.com/xditya/BotStatus/tree/deploy). 5 | 6 | # Secrets. 7 | 8 | Add them to [Settings ⇢ Secrets ⇢ New Repository Secret.](https://docs.github.com/en/actions/reference/encrypted-secrets) 9 | 10 | - `APP_ID` and `API_HASH` from [my.telegram.org](https://my.telegram.org). 11 | - `BOTS` - TG UserName of your bots separated by space. 12 | - `SESSION` - Telethon SessionString of the User to edit the message. 13 | - `REPO_NAME` - yourGitHubUserName/RepositoryName, eg: xditya/BotStatus (fork this repo first). 14 | - `CHANNEL_ID` - ID of your channel. 15 | - `MESSAGE_ID` - ID of the message to edit. 16 | 17 | Optional Variables: 18 | - `CHANNEL_NAME` - Your channel name, with @, eg: `@BotzHub` 19 | - `TIME_ZONE` - Time zone, see available timezone formats [here](https://gist.github.com/heyalexej/8bf688fd67d7199be4a1682b3eec7568). 20 | 21 | # Credits 22 | - [Me](https://xditya.me) 23 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | # (c) @xditya 2 | # This file is a part of https://github.com/xditya/BotStatus 3 | 4 | import asyncio 5 | import logging 6 | import time 7 | import datetime 8 | 9 | import pytz 10 | from decouple import config 11 | from telethon import TelegramClient, functions 12 | from telethon.sessions import StringSession 13 | 14 | # initializing logger 15 | logging.basicConfig( 16 | level=logging.INFO, format="[%(levelname)s] %(asctime)s - %(message)s" 17 | ) 18 | log = logging.getLogger("BotStatus") 19 | 20 | # fetching variales from env 21 | try: 22 | APP_ID = config("APP_ID", cast=int) 23 | API_HASH = config("API_HASH") 24 | SESSION = config("SESSION") 25 | LIST_BOTS = config("BOTS") 26 | CHANNEL_ID = config("CHANNEL_ID", cast=int) 27 | MESSAGE_ID = config("MESSAGE_ID", cast=int) 28 | CHANNEL_NAME = config("CHANNEL_NAME", default="@BotzHub") 29 | TIME_ZONE = config("TIME_ZONE", default="Asia/Kolkata") 30 | except BaseException as ex: 31 | log.info(ex) 32 | exit(1) 33 | 34 | BOTS = LIST_BOTS.split() 35 | 36 | log.info("Connecting bot.") 37 | try: 38 | client = TelegramClient( 39 | StringSession(SESSION), api_id=APP_ID, api_hash=API_HASH 40 | ).start() 41 | except BaseException as e: 42 | log.warning(e) 43 | exit(1) 44 | 45 | 46 | # perform checks 47 | async def check_bots(): 48 | start_time = time.time() 49 | bot_stats = {} 50 | log.info("[CHECK] Started periodic checks...") 51 | channel_current_msg = await client.get_messages(CHANNEL_ID, ids=MESSAGE_ID) 52 | new_message = ( 53 | "⚠️ **New periodic check in progress...** ⚠️\n\n" + channel_current_msg.text 54 | ) 55 | try: 56 | await client.edit_message(CHANNEL_ID, MESSAGE_ID, new_message) 57 | except BaseException as e: 58 | log.warning("[EDIT] Unable to edit message in the channel!") 59 | log.error(e) 60 | 61 | for bot in BOTS: 62 | time_before_sending = time.time() 63 | try: 64 | # send a msg to the bot 65 | sent_msg = await client.send_message(bot, "/start") 66 | await asyncio.sleep(10) 67 | history = await client( 68 | functions.messages.GetHistoryRequest( 69 | peer=bot, 70 | offset_id=0, 71 | offset_date=None, 72 | add_offset=0, 73 | limit=1, 74 | max_id=0, 75 | min_id=0, 76 | hash=0, 77 | ) 78 | ) 79 | if sent_msg.id == history.messages[0].id: 80 | # save stats in a dict 81 | bot_stats[bot] = { 82 | "response_time": None, 83 | "status": "❌", 84 | } 85 | else: 86 | time_after_sending = time.time() 87 | time_taken_for_response = time_after_sending - time_before_sending 88 | 89 | # save stats in a dict. 90 | bot_stats[bot] = { 91 | "response_time": f"`{round(time_taken_for_response * 1000, 3)}ms`", # convert to ms for readability 92 | "status": "✅", 93 | } 94 | except BaseException: 95 | bot_stats[bot] = { 96 | "response_time": "", 97 | "status": "❌", 98 | } 99 | await client.send_read_acknowledge(bot) 100 | log.info(f"[CHECK] Checked @{bot} - {bot_stats[bot]['status']}.") 101 | 102 | end_time = time.time() 103 | total_time_taken = end_time - start_time 104 | log.info("[CHECK] Completed periodic checks.") 105 | 106 | # form the message 107 | status_message = f"**{CHANNEL_NAME}** - __Bot Status__\n\n" 108 | for bot, value in bot_stats.items(): 109 | status_message += ( 110 | f"» {value['status']} @{bot}\n" 111 | if bot_stats[bot]["response_time"] is None 112 | else f"» {bot_stats[bot]['status']} @{bot} [ {bot_stats[bot]['response_time']} ]\n" 113 | ) 114 | 115 | # add time taken to check 116 | hours = int(total_time_taken // 3600) 117 | minutes = int((total_time_taken % 3600) // 60) 118 | seconds = int(total_time_taken % 60) 119 | status_message += f"\n**Checked in** `" 120 | time_added = False 121 | if hours > 0: 122 | time_added = True 123 | status_message += f"{hours}h " 124 | if minutes > 0: 125 | time_added = True 126 | status_message += f"{minutes}m " 127 | if seconds > 0: 128 | time_added = True 129 | status_message += f"{seconds}s " 130 | if not time_added: 131 | status_message += f"{round(total_time_taken * 1000)}ms" 132 | status_message += "`\n" 133 | 134 | # add last checked time 135 | current_time_utc = datetime.datetime.now(pytz.utc) 136 | current_time = current_time_utc.astimezone(pytz.timezone(TIME_ZONE)) 137 | status_message += f"**Last checked at** `{current_time.strftime('%H:%M:%S - %d %B %Y')}` [ __{TIME_ZONE}__ ]" 138 | 139 | # add auto check message 140 | status_message += f"\n\n**This message will be updated every 2 hours.**" 141 | 142 | # edit the message in the channel 143 | try: 144 | await client.edit_message(CHANNEL_ID, MESSAGE_ID, status_message) 145 | except BaseException as e: 146 | log.warning("[EDIT] Unable to edit message in the channel!") 147 | log.error(e) 148 | return 149 | 150 | 151 | client.loop.run_until_complete(check_bots()) 152 | --------------------------------------------------------------------------------