Skip to content

Commit

Permalink
added die cmd (heroku dyno saver)
Browse files Browse the repository at this point in the history
  • Loading branch information
rking32 committed Aug 22, 2020
1 parent c62c70b commit 870a633
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 9 deletions.
1 change: 1 addition & 0 deletions userge/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ class Config:
ALLOWED_COMMANDS: Set[str] = set()
ANTISPAM_SENTRY = False
HEROKU_APP = None
STATUS = None


if Config.HEROKU_API_KEY:
Expand Down
2 changes: 2 additions & 0 deletions userge/core/ext/raw_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

__all__ = ['RawClient']

import time
from typing import Optional

import nest_asyncio
Expand All @@ -21,6 +22,7 @@
class RawClient(Client):
""" userge raw client """
DUAL_MODE = False
LAST_OUTGOING_TIME = time.time()

def __init__(self, bot: Optional['client._UsergeBot'] = None, **kwargs) -> None:
self._bot = bot
Expand Down
9 changes: 7 additions & 2 deletions userge/core/methods/decorators/raw_decorator.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,13 @@ def _clear_cht() -> None:
_TASK_1_START_TO = time.time()


async def _init(r_c: Union['_client.Userge', '_client._UsergeBot']) -> None:
async def _init(r_c: Union['_client.Userge', '_client._UsergeBot'],
r_m: RawMessage) -> None:
global _U_ID, _B_ID # pylint: disable=global-statement
if r_m.from_user and (r_m.from_user.is_self
or (r_m.from_user.id in Config.SUDO_USERS)
or (r_m.from_user.id == Config.OWNER_ID)):
RawClient.LAST_OUTGOING_TIME = time.time()
if _U_ID and _B_ID:
return
if isinstance(r_c, _client.Userge):
Expand Down Expand Up @@ -218,7 +223,7 @@ def _build_decorator(self,
def decorator(func: _PYROFUNC) -> _PYROFUNC:
async def template(r_c: Union['_client.Userge', '_client._UsergeBot'],
r_m: RawMessage) -> None:
await _init(r_c)
await _init(r_c, r_m)
_raise = partial(_raise_func, r_c, r_m.chat.id, r_m.message_id)
if r_m.chat and r_m.chat.type not in flt.scope:
if isinstance(flt, types.raw.Command):
Expand Down
8 changes: 4 additions & 4 deletions userge/core/types/raw/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,10 @@ def parse(cls, command: str, # pylint: disable=arguments-differ
incoming_flt = Filters.create(
lambda _, m:
not m.outgoing
and (
(Config.OWNER_ID and (m.from_user and m.from_user.id == Config.OWNER_ID))
or (Config.SUDO_ENABLED and (cname.lstrip(trigger) in Config.ALLOWED_COMMANDS)
and (m.from_user and m.from_user.id in Config.SUDO_USERS)))
and m.from_user and m.text
and ((m.from_user.id == Config.OWNER_ID)
or (Config.SUDO_ENABLED and (m.from_user.id in Config.SUDO_USERS)
and (cname.lstrip(trigger) in Config.ALLOWED_COMMANDS)))
and (m.text.startswith(Config.SUDO_TRIGGER) if trigger else True))
filters_ = filters_ & (outgoing_flt | incoming_flt)
return cls(_format_about(about), trigger, pattern, filters=filters_, name=cname, **kwargs)
Expand Down
3 changes: 1 addition & 2 deletions userge/plugins/tools/all.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@
@userge.on_cmd("all", about={'header': "list all plugins in plugins/ path"})
async def getplugins(message: Message):
raw_ = get_all_plugins()
all_plugins = ['/'.join(i.split('.')) for i in raw_]
out_str = f"**--({len(raw_)}) Plugins Available!--**\n\n"
for plugin in all_plugins:
for plugin in ('/'.join(i.split('.')) for i in raw_):
out_str += f" `{plugin}.py`\n"
await message.edit(text=out_str, del_in=0)
101 changes: 100 additions & 1 deletion userge/plugins/tools/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,24 @@
import asyncio
import shutil

from userge import userge, Message, Config
from pyrogram import User

from userge import userge, Message, Config, get_collection
from userge.core.ext import RawClient

SAVED_SETTINGS = get_collection("CONFIGS")
MAX_IDLE_TIME = 300
RUN_DYNO_SAVER = False
LOG = userge.getLogger(__name__)
CHANNEL = userge.getCLogger(__name__)


async def _init() -> None:
global MAX_IDLE_TIME, RUN_DYNO_SAVER # pylint: disable=global-statement
d_s = await SAVED_SETTINGS.find_one({'_id': 'DYNO_SAVER'})
if d_s:
RUN_DYNO_SAVER = bool(d_s['on'])
MAX_IDLE_TIME = int(d_s['timeout'])


@userge.on_cmd('restart', about={
Expand Down Expand Up @@ -57,6 +72,35 @@ async def shutdown_(message: Message) -> None:
sys.exit()


@userge.on_cmd("die", about={
'header': "set auto heroku dyno off timeout",
'flags': {'-t': "input offline timeout in min : default to 5min"},
'usage': "{tr}die [flags]",
'examples': ["{tr}die", "{tr}die -t5"]}, allow_channels=False)
async def die_(message: Message) -> None:
global MAX_IDLE_TIME, RUN_DYNO_SAVER # pylint: disable=global-statement
if not Config.HEROKU_APP:
await message.err("`heroku app not detected !`")
return
await message.edit('`processing ...`')
if RUN_DYNO_SAVER:
if isinstance(RUN_DYNO_SAVER, asyncio.Task):
RUN_DYNO_SAVER.cancel()
RUN_DYNO_SAVER = False
SAVED_SETTINGS.update_one({'_id': 'DYNO_SAVER'},
{"$set": {'on': False}}, upsert=True)
await message.edit('auto heroku dyno off worker has been **stopped**',
del_in=5, log=__name__)
return
time_in_min = int(message.flags.get('-t', 5))
MAX_IDLE_TIME = time_in_min * 60
SAVED_SETTINGS.update_one({'_id': 'DYNO_SAVER'},
{"$set": {'on': True, 'timeout': MAX_IDLE_TIME}}, upsert=True)
await message.edit('auto heroku dyno off worker has been **started** '
f'[`{time_in_min}`min]', del_in=3, log=__name__)
RUN_DYNO_SAVER = asyncio.get_event_loop().create_task(_dyno_saver_worker())


@userge.on_cmd("sleep (\\d+)", about={
'header': "sleep userge :P",
'usage': "{tr}sleep [timeout in seconds]"}, allow_channels=False)
Expand All @@ -71,3 +115,58 @@ async def _slp_wrkr(seconds: int) -> None:
await asyncio.sleep(seconds)
await userge.reload_plugins()
await userge.start()


@userge.on_user_status()
async def _user_status(_, user: User) -> None:
Config.STATUS = user.status


@userge.add_task
async def _dyno_saver_worker() -> None:
count = 0
check_delay = 5
offline_start_time = time.time()
while RUN_DYNO_SAVER:
if not count % check_delay:
if Config.STATUS is None or Config.STATUS != "online":
if Config.STATUS is None:
LOG.info("< bot client found ! >")
else:
LOG.info("< state changed to offline ! >")
offline_start_time = time.time()
warned = False
while RUN_DYNO_SAVER and (Config.STATUS is None or Config.STATUS != "online"):
if not count % check_delay:
if Config.STATUS is None:
offline_start_time = RawClient.LAST_OUTGOING_TIME
current_idle_time = int((time.time() - offline_start_time))
if current_idle_time < 5:
warned = False
if current_idle_time >= MAX_IDLE_TIME:
try:
Config.HEROKU_APP.scale_formation_process("worker", 0)
except Exception as h_e: # pylint: disable=broad-except
LOG.err(f"heroku app error : {h_e}")
offline_start_time += 30
continue
LOG.info("< successfully killed heroku dyno ! >")
await CHANNEL.log("heroku dyno killed !")
sys.exit()
return
prog = round(current_idle_time * 100 / MAX_IDLE_TIME, 2)
mins = int(MAX_IDLE_TIME / 60)
if prog >= 75 and not warned:
rem = int((100 - prog) * MAX_IDLE_TIME)
await CHANNEL.log(
f"#WARNING\n\ndyno kill worker `{prog}%` completed !"
f"`{rem}`s remaining !")
warned = True
LOG.info(f"< dyno kill worker ... ({prog}%)({mins}) >")
await asyncio.sleep(1)
count += 1
LOG.info("< state changed to online ! >")
await asyncio.sleep(1)
count += 1
if count:
LOG.info("< auto heroku dyno off worker has been stopped! >")

0 comments on commit 870a633

Please sign in to comment.