示例#1
0
    def limit_reached(update):
        if Cfg.get("rate_limit", "enabled"):
            if update.message:
                uid = update.message.from_user.id
                cmd = update.message.text.split(" ")[0]
            elif update.inline_query:
                uid = update.effective_user.id
                cmd = update.inline_query.query[:-1].split(" ")[0]
            else:
                uid = cmd = None

            if not Cfg.get("rate_limit", "incl_cmd"):
                cmd = None

            rate = Cfg.get("rate_limit", "requests")
            time = Cfg.get("rate_limit", "timespan")

            if RateLimit.reached(uid, rate, time, command=cmd):
                msg = f"{emo.NO_ENTRY} Rate limit ({rate} requests in " \
                      f"{time} seconds) exceeded. Wait a few seconds..."

                update.message.reply_text(msg)
                return True
            return False
        else:
            return False
示例#2
0
    def _update_check(self):
        def _check_for_update(bot, job):
            user = Cfg.get('update', 'github_user')
            repo = Cfg.get('update', 'github_repo')
            gh = GitHub(github_user=user, github_repo=repo)

            try:
                # Get latest release
                response = gh.get_latest_release()
            except Exception as ex:
                logging.error(repr(ex))
                return

            if job.context:
                if job.context["tag"] == response["tag_name"]:
                    return
            else:
                job.context = dict()
                job.context["tag"] = response["tag_name"]

            release_notes = response["body"]

            try:
                response = gh.get_tags()
            except Exception as ex:
                logging.error(repr(ex))
                return

            new_hash = str()
            for t in response:
                if t["name"] == job.context["tag"]:
                    new_hash = t["commit"]["sha"]
                    break

            cfg_hash = Cfg.get("update", "update_hash")

            if cfg_hash != new_hash:
                for admin in Cfg.get("admin_id"):
                    update_cmd = utl.esc_md("/update")
                    tag = job.context['tag']

                    bot.send_message(
                        admin,
                        f"New release *{tag}* available\n\n"
                        f"*Release Notes*\n{release_notes}\n\n"
                        f"{update_cmd}",
                        parse_mode=ParseMode.MARKDOWN)

        if Cfg.get("update", "update_check") is not None:
            sec = utl.get_seconds(Cfg.get("update", "update_check"))

            if not sec:
                sec = con.DEF_UPDATE_CHECK
                msg = f"Update check time not valid. Using {sec} seconds"
                logging.warning(msg)

            try:
                self.job_queue.run_repeating(_check_for_update, sec, first=0)
            except Exception as e:
                logging.error(repr(e))
示例#3
0
 def bot_start_webhook(self):
     self.updater.start_webhook(listen=Cfg.get("webhook", "listen"),
                                port=Cfg.get("webhook", "port"),
                                url_path=self.token,
                                key=Cfg.get("webhook", "privkey_path"),
                                cert=Cfg.get("webhook", "cert_path"),
                                webhook_url=f"{Cfg.get('webhook', 'url')}:"
                                f"{Cfg.get('webhook', 'port')}/"
                                f"{self.token}")
示例#4
0
    def _refresh_cache(self):
        if Cfg.get("refresh_cache") is not None:
            sec = utl.get_seconds(Cfg.get("refresh_cache"))

            if not sec:
                sec = con.DEF_CACHE_REFRESH
                msg = f"Refresh rate for caching not valid. Using {sec} seconds"
                logging.warning(msg)

            try:
                self.job_queue.run_repeating(APICache.refresh, sec, first=0)
            except Exception as e:
                logging.error(repr(e))
示例#5
0
        def _check_for_update(bot, job):
            user = Cfg.get('update', 'github_user')
            repo = Cfg.get('update', 'github_repo')
            gh = GitHub(github_user=user, github_repo=repo)

            try:
                # Get latest release
                response = gh.get_latest_release()
            except Exception as ex:
                logging.error(repr(ex))
                return

            if job.context:
                if job.context["tag"] == response["tag_name"]:
                    return
            else:
                job.context = dict()
                job.context["tag"] = response["tag_name"]

            release_notes = response["body"]

            try:
                response = gh.get_tags()
            except Exception as ex:
                logging.error(repr(ex))
                return

            new_hash = str()
            for t in response:
                if t["name"] == job.context["tag"]:
                    new_hash = t["commit"]["sha"]
                    break

            cfg_hash = Cfg.get("update", "update_hash")

            if cfg_hash != new_hash:
                for admin in Cfg.get("admin_id"):
                    update_cmd = utl.esc_md("/update")
                    tag = job.context['tag']

                    try:
                        bot.send_message(
                            admin,
                            f"New release *{utl.esc_md(tag)}* available\n\n"
                            f"*Release Notes*\n{utl.esc_md(release_notes)}\n\n"
                            f"{update_cmd}",
                            parse_mode=ParseMode.MARKDOWN)
                    except Exception as ex:
                        err = f"Can't send release notes to chat {admin}"
                        logging.error(f"{err} - {ex}")
示例#6
0
    def __init__(self, telegram_bot):
        super().__init__(telegram_bot)

        # Check if database is enabled since this plugin needs it
        if not Cfg.get("database", "use_db"):
            msg = f"Plugin '{type(self).__name__}' " \
                  f"can't be used since database is disabled"
            logging.warning(msg)
示例#7
0
    def __init__(self, bot_token, bot_db):
        self.db = bot_db
        self.token = bot_token

        read_timeout = Cfg.get("telegram", "read_timeout")
        connect_timeout = Cfg.get("telegram", "connect_timeout")

        kwargs = dict()
        if read_timeout:
            kwargs["read_timeout"] = read_timeout
        if connect_timeout:
            kwargs["connect_timeout"] = connect_timeout

        try:
            self.updater = Updater(bot_token, request_kwargs=kwargs)
        except InvalidToken as e:
            cls_name = f"Class: {type(self).__name__}"
            logging.error(f"{repr(e)} - {cls_name}")
            exit("ERROR: Bot token not valid")

        self.job_queue = self.updater.job_queue
        self.dispatcher = self.updater.dispatcher

        # Load classes in folder 'plugins'
        self._load_plugins()

        # Handler for files downloads (plugins)
        mh = MessageHandler(Filters.document, self._download)
        self.dispatcher.add_handler(mh)

        # Handler for command-links
        self._add_link_handler()

        # Handler for inline-mode
        inline_handler = InlineQueryHandler(self._inline)
        self.dispatcher.add_handler(inline_handler)

        # Handle all Telegram related errors
        self.dispatcher.add_error_handler(self._handle_tg_errors)

        # Refresh cache periodically
        self._refresh_cache()

        # Check for updates periodically
        self._update_check()
示例#8
0
    def _restart_notification(self):
        cls_name = type(self).__name__.lower()
        message = Cfg.get("plugins", cls_name, "message")
        user_id = Cfg.get("plugins", cls_name, "user_id")

        if not message or not user_id:
            return

        try:
            self.tgb.updater.bot.edit_message_text(
                chat_id=user_id,
                message_id=message,
                text=f"{emo.CHECK} Restarting bot...")
        except Exception as e:
            msg = "Not possible to update restart message"
            logging.error(f"{msg}: {e}")

        Cfg.remove("plugins", cls_name)
示例#9
0
    def after_plugins_loaded(self):
        if Cfg.get("database", "use_db"):
            for repeater in self.tgb.db.read_rep() or []:
                interval = repeater[4]
                update = repeater[5]

                try:
                    self._run_repeater(update, int(interval))
                except Exception as e:
                    update.message.reply_text(text=f"{emo.ERROR} {e}",
                                              parse_mode=ParseMode.MARKDOWN)
示例#10
0
        def _save_data(self, bot, update, **kwargs):
            if Cfg.get("database", "use_db"):
                if update.message:
                    usr = update.message.from_user
                    cmd = update.message.text
                    cht = update.message.chat
                elif update.inline_query:
                    usr = update.effective_user
                    cmd = update.inline_query.query[:-1]
                    cht = update.effective_chat
                else:
                    logging.warning(f"Can't save usage - {update}")
                    return func(self, bot, update, **kwargs)

                if usr.id in Cfg.get("admin_id"):
                    if not Cfg.get("database", "track_admins"):
                        return func(self, bot, update, **kwargs)

                self.tgb.db.save(usr, cht, cmd)

            return func(self, bot, update, **kwargs)
示例#11
0
    def execute(self, sql, *args):
        if Cfg.get("database", "use_db"):
            con = sqlite3.connect(self._db_path)
            cur = con.cursor()

            cur.execute(sql, args)
            con.commit()

            result = cur.fetchall()

            con.close()
            return result
示例#12
0
    def __init__(self, bot_token, bot_db):
        self.db = bot_db
        self.token = bot_token

        read_timeout = Cfg.get("telegram", "read_timeout")
        connect_timeout = Cfg.get("telegram", "connect_timeout")

        kwargs = dict()
        if read_timeout:
            kwargs["read_timeout"] = read_timeout
        if connect_timeout:
            kwargs["connect_timeout"] = connect_timeout

        try:
            self.updater = Updater(bot_token, request_kwargs=kwargs)
        except InvalidToken:
            exit("ERROR: Bot token not valid")

        self.job_queue = self.updater.job_queue
        self.dispatcher = self.updater.dispatcher

        # Handler for command-links
        self._add_link_handler()

        # Load classes in folder 'plugins'
        self._load_plugins()

        # Handler for inline-mode
        self.dispatcher.add_handler(InlineQueryHandler(self._inline))

        # Handle all Telegram related errors
        self.dispatcher.add_error_handler(self._handle_tg_errors)

        # Refresh cache periodically if enabled
        self._refresh_cache()

        # Check for updates periodically
        self._update_check()
示例#13
0
    def _download(self, bot, update):
        if update.effective_user.id not in Cfg.get("admin_id"):
            return

        name = update.message.effective_attachment.file_name
        file = bot.getFile(update.message.document.file_id)
        file.download(os.path.join(con.SRC_DIR, con.PLG_DIR, name))

        try:
            self.reload_plugin(name.replace(".py", ""))
            update.message.reply_text(
                f"{emo.CHECK} Plugin loaded successfully")
        except Exception as ex:
            update.message.reply_text(f"{emo.ERROR} {ex}")
示例#14
0
    def get_action(self, bot, update, args):

        if not args:
            update.message.reply_text(text=f"Usage:\n{self.get_usage()}",
                                      parse_mode=ParseMode.MARKDOWN)
            return

        user = update.message.from_user
        if user.username:
            name = f"@{user.username}"
        else:
            name = user.first_name

        feedback = update.message.text.replace(f"/{self.get_cmd()} ", "")

        for admin in Cfg.get("admin_id"):
            bot.send_message(admin, f"Feedback from {name}: {feedback}")

        update.message.reply_text(f"Thanks for letting us know {emo.TOP}")
示例#15
0
    def _download(self, bot, update):
        # Check if in a private chat
        if bot.get_chat(update.message.chat_id).type != Chat.PRIVATE:
            return

        # Check if user that triggered the command is allowed to execute it
        if update.effective_user.id not in Cfg.get("admin_id"):
            return

        name = update.message.effective_attachment.file_name
        file = bot.getFile(update.message.document.file_id)
        file.download(os.path.join(con.SRC_DIR, con.PLG_DIR, name))

        try:
            self.reload_plugin(name.replace(".py", ""))
            update.message.reply_text(
                f"{emo.CHECK} Plugin loaded successfully")
        except Exception as ex:
            update.message.reply_text(f"{emo.ERROR} {ex}")
示例#16
0
    def __init__(self):
        # Parse command line arguments
        self.args = self._parse_args()

        # Load config file
        Cfg(self.args.config)

        # Set up logging
        log_path = self.args.logfile
        log_level = self.args.loglevel
        self._init_logger(log_path, log_level)

        if Cfg.get("use_db"):
            # Create database
            sql_dir = con.SQL_DIR
            db_path = self.args.database
            self.db = Database(db_path, sql_dir)
        else:
            self.db = None

        # Create bot
        bot_token = self._get_bot_token()
        self.tg = TelegramBot(bot_token, self.db)
示例#17
0
 def _only_owner(self, bot, update, **kwargs):
     if update.effective_user.id in Cfg.get("admin_id"):
         return func(self, bot, update, **kwargs)
示例#18
0
    def get_action(self, bot, update, args):
        restart = False
        force = False
        check = False

        if "force" in args:
            force = True
        if "check" in args:
            check = True
        if "restart" in args:
            restart = True

        if force and check:
            msg = f"{emo.ERROR} Combination of 'force' " \
                  f"and 'check' arguments not allowed"
            update.message.reply_text(msg)
            return

        kw = utl.get_kw(args)
        branch = kw.get("branch", None)
        release = kw.get("release", None)

        if branch and release:
            msg = f"{emo.ERROR} Combination of 'branch' " \
                  f"and 'release' arguments not allowed"
            update.message.reply_text(msg)
            return

        msg = f"{emo.WAIT} Check for update..."
        m = update.message.reply_text(msg)

        user = Cfg.get('update', 'github_user')
        repo = Cfg.get('update', 'github_repo')
        gh = GitHub(github_user=user, github_repo=repo)

        uid = update.message.from_user.id
        download_url = str()

        try:
            # Clean old update data if present
            shutil.rmtree(os.path.join(os.getcwd(), con.UPD_DIR))
        except:
            pass

        # ---------- BRANCH ----------
        if branch:
            try:
                # Get latest commit info for branch
                response = gh.get_latest_branch(branch)
            except Exception as e:
                return self.handle_error(e, update)

            cfg_hash = Cfg.get("update", "update_hash")
            new_hash = response["commit"]["sha"]

            msg = f"{emo.CHECK} Check for update..."
            bot.edit_message_text(msg, chat_id=uid, message_id=m.message_id)

            if cfg_hash == new_hash and not force:
                msg = f"{emo.CHECK} You are already running the latest version"
                update.message.reply_text(msg)
                return

            if check:
                msg = f"{emo.CHECK} New branch commits available!"
                update.message.reply_text(msg)
                return

            # Get latest version of branch as zip
            download_url = f"https://github.com/{user}/{repo}/archive/{branch}.zip"

        # ---------- RELEASE ----------
        else:
            try:
                if release:
                    # Get specific release
                    response = gh.get_releases()
                else:
                    # Get latest release
                    response = gh.get_latest_release()
            except Exception as e:
                return self.handle_error(e, update)

            if release:
                tag = response[0]["tag_name"]
                release_notes = response[0]["body"]
            else:
                tag = response["tag_name"]
                release_notes = response["body"]

            try:
                response = gh.get_tags()
            except Exception as e:
                return self.handle_error(e, update)

            new_hash = str()
            for t in response:
                if t["name"] == tag:
                    new_hash = t["commit"]["sha"]
                    download_url = t["zipball_url"]
                    break

            if not new_hash:
                msg = f"{emo.ERROR} Tag '{tag}' unknown"
                update.message.reply_text(msg)
                return

            cfg_hash = Cfg.get("update", "update_hash")

            msg = f"{emo.CHECK} Check for update..."
            bot.edit_message_text(msg, chat_id=uid, message_id=m.message_id)

            if cfg_hash == new_hash and not force:
                msg = f"{emo.CHECK} You are already running this release"
                update.message.reply_text(msg)
                return

            if check:
                msg = f"{emo.CHECK} New release *{tag}* available!\n\n" \
                      f"*Release Notes*\n{release_notes}"
                update.message.reply_text(msg, parse_mode=ParseMode.MARKDOWN)
                return

        # ---------- DOWNLOAD & UPDATE ----------

        msg = f"{emo.WAIT} Downloading update..."
        m = update.message.reply_text(msg)

        try:
            response = requests.get(download_url)
            response.raise_for_status()
        except Exception as e:
            return self.handle_error(e, update)

        msg = f"{emo.CHECK} Downloading update..."
        bot.edit_message_text(msg, chat_id=uid, message_id=m.message_id)

        msg = f"{emo.WAIT} Updating bot..."
        m = update.message.reply_text(msg)

        zip_file = zipfile.ZipFile(io.BytesIO(response.content))
        zip_file.extractall(con.UPD_DIR)

        done = False
        unzip_dir = str()
        for _, dirs, _ in os.walk(con.UPD_DIR):
            for d in dirs:
                unzip_dir = d
                done = True
                break
            if done:
                break

        self._update_bot(os.path.join(con.UPD_DIR, unzip_dir))

        Cfg.set(new_hash, "update", "update_hash")

        msg = f"{emo.CHECK} Updating bot..."
        bot.edit_message_text(msg, chat_id=uid, message_id=m.message_id)

        if restart:
            msg = f"{emo.WAIT} Restarting bot..."
            update.message.reply_text(msg)

            time.sleep(0.2)
            os.execl(sys.executable, sys.executable, *sys.argv)
        else:
            msg = "Bot /restart needed"
            update.message.reply_text(msg)
示例#19
0
    def get_action(self, bot, update, args):
        if args:
            command = args[0].lower()

            # Execute raw SQL
            if command == "sql":
                if Cfg.get("database", "use_db"):
                    args.pop(0)

                    sql = " ".join(args)
                    data = self.tgb.db.execute_sql(sql)

                    if data["error"]:
                        msg = data["error"]
                    elif data["result"]:
                        msg = '\n'.join(str(s) for s in data["result"])
                    else:
                        msg = f"{emo.INFO} No data returned"

                    update.message.reply_text(msg)
                else:
                    update.message.reply_text(
                        f"{emo.INFO} Database not enabled")

            # Change configuration
            elif command == "cfg":
                args.pop(0)
                v = args[-1]
                v = v.lower()
                args.pop(-1)

                # Convert to boolean
                if v == "true" or v == "false":
                    v = utl.str2bool(v)

                # Convert to integer
                elif v.isnumeric():
                    v = int(v)

                # Convert to null
                elif v == "null" or v == "none":
                    v = None

                try:
                    Cfg.set(v, *args)
                except Exception as e:
                    return self.handle_error(e, update)

                update.message.reply_text("Config changed")

            # Send global message
            elif command == "msg":
                if Cfg.get("database", "use_db"):
                    args.pop(0)

                    sql = self.get_sql("global_msg")
                    data = self.tgb.db.execute_sql(sql)

                    title = "This is a global message to " \
                            "every user of @OpenCryptoBot:\n\n"

                    msg = " ".join(args)

                    for user_id in data or []:
                        try:
                            bot.send_message(chat_id=user_id[0],
                                             text=f"{title}{msg}")
                        except Exception as e:
                            self.handle_error(e, update, send_error=False)
                else:
                    update.message.reply_text(
                        f"{emo.INFO} Database not enabled")

            # Manage plugins
            elif command == "plg":
                args.pop(0)

                # LOAD plugin
                if args[0].lower() == "load":
                    self.tgb.reload_plugin(args[1])
                    update.message.reply_text("Plugin loaded")

                # UNLOAD plugin
                elif args[0].lower() == "unload":
                    self.tgb.remove_plugin(args[1])
                    update.message.reply_text("Plugin unloaded")

        else:
            usr = update.effective_user.first_name

            update.message.reply_text(
                text=f"Welcome {usr}.\nChoose a statistic",
                reply_markup=self._keyboard_stats())
示例#20
0
    def _callback(self, bot, update):
        query = update.callback_query

        if not Cfg.get("database", "use_db"):
            bot.answer_callback_query(query.id, text="Database not enabled")
            return

        # Statistics - Number of Commands
        if query.data == "admin_cmds":
            data = self.tgb.db.execute_sql(self.get_sql("number_cmd"))

            if data["error"]:
                msg = data["error"]
            elif data["result"]:
                msg = f"`Commands: {data['result'][0][0]}`"
            else:
                msg = f"{emo.INFO} No data returned"

            bot.send_message(text=msg,
                             chat_id=update.effective_user.id,
                             parse_mode=ParseMode.MARKDOWN)

        # Statistics - Number of Users
        elif query.data == "admin_usrs":
            data = self.tgb.db.execute_sql(self.get_sql("number_usr"))

            if data["error"]:
                msg = data["error"]
            elif data["result"]:
                msg = f"`Users: {data['result'][0][0]}`"
            else:
                msg = f"{emo.INFO} No data returned"

            bot.send_message(text=msg,
                             chat_id=update.effective_user.id,
                             parse_mode=ParseMode.MARKDOWN)

        # Statistics - Command Toplist
        elif query.data == "admin_cmdtop":
            data = self.tgb.db.execute_sql(self.get_sql("cmd_top"))

            msg = str()
            if data["error"]:
                msg = data["error"]
            elif data["result"]:
                for row in data["result"] or []:
                    msg += utl.esc_md(f"{row[1]} {row[0]}\n")
            else:
                msg = f"{emo.INFO} No data returned"

            bot.send_message(text=f"`Command Toplist:\n\n{msg}`",
                             chat_id=update.effective_user.id,
                             parse_mode=ParseMode.MARKDOWN)

        # Statistics - Language Toplist
        elif query.data == "admin_langtop":
            data = self.tgb.db.execute_sql(self.get_sql("lang_top"))

            msg = str()
            if data["error"]:
                msg = data["error"]
            elif data["result"]:
                for row in data["result"] or []:
                    msg += f"{row[1]} {row[0]}\n"
            else:
                msg = f"{emo.INFO} No data returned"

            bot.send_message(text=f"`Language Toplist:\n\n{msg}`",
                             chat_id=update.effective_user.id,
                             parse_mode=ParseMode.MARKDOWN)

        # Statistics - User Toplist
        elif query.data == "admin_usertop":
            data = self.tgb.db.execute_sql(self.get_sql("user_top"))

            msg = str()
            if data["error"]:
                msg = data["error"]
            elif data["result"]:
                for row in data["result"] or []:
                    msg += f"{row[1]} {row[0]}\n"
            else:
                msg = f"{emo.INFO} No data returned"

            bot.send_message(text=f"`User Toplist:\n\n{msg}`",
                             chat_id=update.effective_user.id,
                             parse_mode=ParseMode.MARKDOWN)

        # Statistics - Daily Users
        elif query.data == "admin_userdaily":
            data = self.tgb.db.execute_sql(self.get_sql("user_daily"))

            if data["error"]:
                msg = data["error"]
            elif data["result"]:
                o_dict = OrderedDict()
                for row in data["result"] or []:
                    date = row[0].split(" ")[0]
                    if date not in o_dict:
                        o_dict[date] = list()
                    o_dict[date].append(row[1])

                msg = str()
                for k, v in o_dict.items():
                    msg += f"\n{k}\n"
                    for name in v:
                        msg += f"{name}\n"
            else:
                msg = f"{emo.INFO} No data returned"

            bot.send_message(text=f"`Daily Users:\n{msg}`",
                             chat_id=update.effective_user.id,
                             parse_mode=ParseMode.MARKDOWN)

        bot.answer_callback_query(query.id, text="Query executed")
示例#21
0
    def get_action(self, bot, update, args):
        # Check if database is enabled
        if not Cfg.get("database", "use_db"):
            update.message.reply_text(
                text=f"{emo.ERROR} Plugin '{type(self).__name__}' "
                f"can't be used since database is disabled",
                parse_mode=ParseMode.MARKDOWN)
            return

        # Check if any arguments provided
        if not args:
            update.message.reply_text(text=f"Usage:\n{self.get_usage()}",
                                      parse_mode=ParseMode.MARKDOWN)
            return

        # 'list' argument - show all repeaters for a user
        if args[0].lower() == "list":
            chat_id = update.message.chat.id
            user_id = update.message.from_user.id

            repeaters = self.tgb.db.read_rep(user_id, chat_id)

            if repeaters:
                for rep in repeaters:
                    chat = self.tgb.db.read_chat(rep[2])
                    chat_name = chat[2] if chat else None

                    update.message.reply_text(
                        text=f"Command:\n`{rep[3]}`\n"
                        f"Chat:\n`{chat_name}`\n\n"
                        f"↺ {rep[4]} seconds\n\n"
                        f"(ID: {rep[0]})",
                        parse_mode=ParseMode.MARKDOWN,
                        reply_markup=self._keyboard_remove_rep())
                return
            else:
                update.message.reply_text(f"{emo.INFO} No repeaters active")
                return

        # Extract time interval
        interval = str()
        if args[0].startswith("i="):
            interval = args[0].replace("i=", "")
            args.pop(0)

        # Check if time interval is provided
        if not interval:
            update.message.reply_text(
                text=f"{emo.ERROR} Time interval has to be provided",
                parse_mode=ParseMode.MARKDOWN)
            return

        # In seconds
        interval = utl.get_seconds(interval)

        # Check if interval was successfully converted to seconds
        if not interval:
            update.message.reply_text(
                text=f"{emo.ERROR} Wrong format for time interval",
                parse_mode=ParseMode.MARKDOWN)
            return

        # Check for command to repeat
        if not args:
            update.message.reply_text(
                text=f"{emo.ERROR} Provide command to repeat",
                parse_mode=ParseMode.MARKDOWN)
            return

        # Check if command is repeater itself
        if args[0].replace("/", "") in self.get_cmds():
            update.message.reply_text(
                text=f"{emo.ERROR} Repeater can't repeat itself",
                parse_mode=ParseMode.MARKDOWN)
            return

        # Set command to repeat as current message text
        update.message.text = " ".join(args)

        try:
            self._run_repeater(update, interval)
            self.tgb.db.save_rep(update, interval)
        except IntegrityError as ie:
            err = "Repeater already saved"
            update.message.reply_text(f"{emo.ERROR} {err}")
            logging.warning(f"{err} {ie}")
            return
        except Exception as e:
            update.message.reply_text(f"{emo.ERROR} {e}")
            raise e

        update.message.reply_text(text=f"{emo.CHECK} Timer is active")