def search_bots(query): query = query.lower().strip() split = query.split(' ') # easter egg if query in ('awesome bot', 'great bot', 'superb bot', 'best bot', 'best bot ever'): return [Bot.by_username('@botlistbot')] # exact results where_query = ((fn.lower(Bot.username).contains(query) | fn.lower(Bot.name) << split | fn.lower(Bot.extra)**query) & (Bot.revision <= Revision.get_instance().nr & Bot.approved == True & Bot.disabled == False)) results = set(Bot.select().distinct().where(where_query)) # keyword results keyword_results = Bot.select(Bot).join( Keyword).where((fn.lower(Keyword.name) << split) & (Bot.revision <= Revision.get_instance().nr) & (Bot.approved == True & Bot.disabled == False)) results.update(keyword_results) # many @usernames usernames = re.findall(settings.REGEX_BOT_ONLY, query) if usernames: try: bots = Bot.many_by_usernames(usernames) print([b.username for b in bots]) results.update(bots) except Bot.DoesNotExist: pass return list(results)
def lookup_entity(query, exact=True): """ Searches for a Bot or User contained in the query """ if exact: try: return Bot.by_username(query, include_disabled=True) except Bot.DoesNotExist: pass try: return Bot.get(chat_id=int(query)) except ValueError: pass except Bot.DoesNotExist: pass try: return User.by_username(query) except User.DoesNotExist: pass try: return User.get(chat_id=query) except User.DoesNotExist: pass return None
def _new_bots_text(): new_bots = Bot.select_new_bots() if len(new_bots) > 0: txt = "Fresh new bots since the last update 💙:\n\n{}".format( Bot.get_new_bots_markdown() ) else: txt = "No new bots available." return txt
def _admin_buttons(send_botlist_button=False, logs_button=False): n_unapproved = len(Bot.select().where(Bot.approved == False, Bot.disabled == False)) n_suggestions = len(Suggestion.select_all()) n_pending = len(Bot.select_pending_update()) second_row = list() if n_unapproved > 0: second_row.append( KeyboardButton( captions.APPROVE_BOTS + " {}🆕".format(mdformat.number_as_emoji(n_unapproved)) ) ) if n_suggestions > 0: second_row.append( KeyboardButton( captions.APPROVE_SUGGESTIONS + " {}⁉️".format(mdformat.number_as_emoji(n_suggestions)) ) ) buttons = [ [KeyboardButton(captions.EXIT), KeyboardButton(captions.REFRESH)], [ KeyboardButton(captions.FIND_OFFLINE), KeyboardButton(captions.SEND_CONFIG_FILES), ], ] update_row = list() if n_pending > 0: update_row.append( KeyboardButton( captions.PENDING_UPDATE + " {}{}".format( mdformat.number_as_emoji(n_pending), captions.SUGGESTION_PENDING_EMOJI, ) ) ) if send_botlist_button: update_row.append(KeyboardButton(captions.SEND_BOTLIST)) if logs_button: update_row.append(KeyboardButton(captions.SEND_ACTIVITY_LOGS)) if len(update_row) > 0: buttons.insert(1, update_row) if len(second_row) > 0: buttons.insert(1, second_row) return buttons
def update_bot_details(self, to_check: BotModel, peer): """ Set basic properties of the bot """ if isinstance(peer, ResolvedPeer): peer = self.resolve_peer(peer.peer.user_id) elif isinstance(peer, InputPeerUser): pass else: peer = self.resolve_peer(peer.id) try: user = self.send(GetUsers([peer]))[0] except: traceback.print_exc() print("this peer does not work for GetUsers:") print(type(peer)) print(peer) return None if hasattr(user, 'bot') and user.bot is True: # Regular bot to_check.official = bool(user.verified) to_check.inlinequeries = bool(user.bot_inline_placeholder) to_check.name = user.first_name to_check.bot_info_version = user.bot_info_version else: # Userbot to_check.userbot = True to_check.name = helpers.format_name(user) # In any case to_check.chat_id = int(user.id) to_check.username = '******' + str(user.username)
def send_offline(bot, update): chat_id = util.uid_from_update(update) offline = ( Bot.select() .where(Bot.offline == True, Bot.disabled == False) .order_by(Bot.last_response.asc()) ) def offline_since(b): if not b.last_response: return "a long time" slanged_time = helpers.slang_datetime(b.last_response) return slanged_time.replace(" ago", "") if len(offline) > 0: text = "Offline Bots:\n\n" text += "\n".join( [ "{}{} — /edit{}".format( str(b), " (for {})".format(offline_since(b)), b.id ) for b in offline ] ) else: text = "No bots are offline." bot.formatter.send_message(chat_id, text)
def send_footer(self): num_bots = Bot.select_approved().count() self.notify_admin('Sending footer...') # add footer as notification footer = '\n```' footer += '\n' + mdformat.centered("• @BotList •\n{}\n{} bots".format( datetime.date.today().strftime("%Y-%m-%d"), num_bots)) footer += '```' if self.resend or not self.silent: try: self._delete_message(self.channel.footer_mid) except BadRequest as e: pass footer_to_edit = None else: footer_to_edit = self.channel.footer_mid footer_msg = self.bot.formatter.send_or_edit( self.channel.chat_id, footer, to_edit=footer_to_edit, timeout=120, disable_notifications=self.silent, reply_markup=self.portal_markup) if footer_msg: self.channel.footer_mid = footer_msg.message_id self.sent['footer'] = "Footer sent" self._save_channel()
def ping_bots_job(bot, job): bot_checker: BotChecker = job.context.get('checker') stop_event: threading.Event = job.context.get('stop') loop = bot_checker.event_loop all_bots = BotModel.select(BotModel).where( (BotModel.approved == True) & ((BotModel.disabled_reason == BotModel.DisabledReason.offline) | BotModel.disabled_reason.is_null()) ).order_by( BotModel.last_ping.asc() ) start = time.time() result = loop.run_until_complete(run(bot, bot_checker, all_bots, stop_event)) # type: Counter end = time.time() if not result: msg = "👎 BotChecker encountered problems." else: msg = "ℹ️ BotChecker completed in {}s:\n".format(round(end - start)) for k, v in result.items(): msg += "\n● {} {}".format(v, k) bot.send_message(settings.BOTLIST_NOTIFICATIONS_ID, msg) log.info(msg)
def edit_bot(bot, update, chat_data, to_edit=None): uid = util.uid_from_update(update) message_id = util.mid_from_update(update) user = User.from_update(update) if not to_edit: if update.message: command = update.message.text if "edit" in command: b_id = re.match(r"^/edit(\d+)$", command).groups()[0] elif "approve" in command: b_id = re.match(r"^/approve(\d+)$", command).groups()[0] else: raise ValueError("No 'edit' or 'approve' in command.") try: to_edit = Bot.get(id=b_id) except Bot.DoesNotExist: update.message.reply_text(util.failure("No bot exists with this id.")) return else: bot.formatter.send_failure(uid, "An unexpected error occured.") return # if not to_edit.approved: # return approve_bots(bot, update, override_list=[to_edit]) pending_suggestions = Suggestion.pending_for_bot(to_edit, user) reply_markup = InlineKeyboardMarkup( _edit_bot_buttons(to_edit, pending_suggestions, uid in settings.MODERATORS) ) pending_text = ( "\n\n{} Some changes are pending approval{}.".format( captions.SUGGESTION_PENDING_EMOJI, "" if user.chat_id in settings.MODERATORS else " by a moderator", ) if pending_suggestions else "" ) meta_text = ( "\n\nDate added: {}\nMember since revision {}\n" "Submitted by {}\nApproved by {}".format( to_edit.date_added, to_edit.revision, to_edit.submitted_by, to_edit.approved_by, ) ) bot.formatter.send_or_edit( uid, "🛃 Edit {}{}{}".format( to_edit.detail_text, meta_text if user.id in settings.MODERATORS else "", pending_text, ), to_edit=message_id, reply_markup=reply_markup, )
def test_new(client: BotIntegrationClient): uname = '@test__bot' try: try: b = Bot.get(username=uname) b.delete_instance() except Bot.DoesNotExist: pass res = client.send_command_await("new", [uname]) if client.get_me().id in settings.MODERATORS: assert 'is currently pending' in res.full_text.lower() assert res.inline_keyboards[0].find_button(r'.*Accept.*') else: assert re.search('you submitted.*for approval', res.full_text, re.IGNORECASE) finally: Bot.delete().where(Bot.username == uname)
def random_bot(): """ Returns a random bot from the BotList. By default, only "interesting" bots with description and tags are shown. Use the parameter `?all=True` to receive _all_ possible choices. """ show_all = bool(request.args.get("all", False)) if show_all: random_bot = Bot.select().order_by(fn.Random()).limit(1)[0] else: random_bot = random.choice(Bot.explorable_bots()) data = random_bot.serialize if data: res = jsonify({'search_result': data, 'meta': {'url': request.url}}) res.status_code = 200 else: res = _error("No bot found.") return res
def apply_all_changes(bot, update, chat_data, to_edit): user = User.from_update(update) user_suggestions = Suggestion.select_all_of_user(user) for suggestion in user_suggestions: suggestion.apply() refreshed_bot = Bot.get(id=to_edit.id) edit_bot(bot, update, chat_data, refreshed_bot) Statistic.of(update, "apply", refreshed_bot.username)
def explore(bot, update, chat_data): cid = update.effective_chat.id uid = update.effective_user.id mid = util.mid_from_update(update) explorable_bots = Bot.explorable_bots() chat_data["explored"] = chat_data.get("explored", list()) # don't explore twice for explored in chat_data["explored"]: explorable_bots.remove(explored) if len(explorable_bots) == 0: util.send_md_message( bot, cid, mdformat.none_action( "You have explored all the bots. Congratulations, you might be the first 😜" ), ) return random_bot = random.choice(explorable_bots) buttons = [ [ InlineKeyboardButton( captions.ADD_TO_FAVORITES, callback_data=util.callback_for_action( CallbackActions.ADD_TO_FAVORITES, {"id": random_bot.id} ), ), InlineKeyboardButton( captions.SHARE, switch_inline_query=random_bot.username ), ], [ InlineKeyboardButton( random_explore_text(), callback_data=util.callback_for_action(CallbackActions.EXPLORE_NEXT), ) ], ] markup = InlineKeyboardMarkup(buttons) text = random_bot.detail_text if uid in settings.MODERATORS and util.is_private_message(update): text += "\n\n🛃 /edit{}".format(random_bot.id) msg = bot.formatter.send_or_edit(cid, text, to_edit=mid, reply_markup=markup) chat_data["explored"].append(random_bot)
def t3chnostats(bot, update): days = 30 txt = 'Bots approved by other people *in the last {} days*:\n\n'.format( days) bots = Bot.select().where( (Bot.approved_by != User.get(User.chat_id == 918962)) & (Bot.date_added.between( datetime.date.today() - datetime.timedelta(days=days), datetime.date.today()))) txt += '\n'.join( ['{} by @{}'.format(str(b), b.approved_by.username) for b in bots]) update.message.reply_text(txt, parse_mode=ParseMode.MARKDOWN)
def update_new_bots_list(self): text = self._read_file(self.NEW_BOTS_FILE) # insert spaces and the name of the bot new_bots_joined = Bot.get_new_bots_markdown() text = text.format(new_bots_joined) msg = self.send_or_edit(text, self.channel.new_bots_mid) self.sent['new_bots_list'] = "List of new bots sent" if msg: self.channel.new_bots_mid = msg.message_id self._save_channel()
def ban_bot(bot, update, chat_data, to_ban: Bot, ban_state: bool): if to_ban.disabled and ban_state is True: update.message.reply_text( mdformat.none_action("{} is already banned.".format(to_ban)), parse_mode="markdown", ) return if not to_ban.disabled and ban_state is False: update.message.reply_text( mdformat.none_action("{} is not banned.".format(to_ban)), parse_mode="markdown", ) return if ban_state: to_ban.disable(Bot.DisabledReason.banned) update.message.reply_text("Bot was banned.") else: to_ban.enable() update.message.reply_text("Bot was unbanned.") to_ban.save() from botlistbot.components.explore import send_bot_details return send_bot_details(bot, update, chat_data, to_ban)
def add_favorite_handler(bot, update, args=None): uid = util.uid_from_update(update) from botlistbot.components.basic import main_menu_buttons main_menu_markup = ReplyKeyboardMarkup( main_menu_buttons(uid in settings.MODERATORS)) if args: query = ' '.join(args) if isinstance(args, list) else args try: # TODO: add multiple username = re.match(settings.REGEX_BOT_IN_TEXT, query).groups()[0] try: # TODO: get exact database matches for input without `@` item = Bot.by_username(username, include_disabled=True) return add_favorite(bot, update, item) except Bot.DoesNotExist: buttons = [ InlineKeyboardButton( "Yai!", callback_data=util.callback_for_action( CallbackActions.ADD_ANYWAY, {'u': username})), InlineKeyboardButton( "Nay...", callback_data=util.callback_for_action( CallbackActions.ADD_FAVORITE)) ] reply_markup = InlineKeyboardMarkup([buttons]) util.send_md_message( bot, uid, "{} is not in the @BotList. Do you want to add it to your {} anyway?" .format(username, captions.FAVORITES), reply_markup=reply_markup) except AttributeError: # invalid bot username # TODO when does this happen? update.message.reply_text( util.failure( "Sorry, but that is not a valid username. Please try again. /addfav" )) else: buttons = [ InlineKeyboardButton("Search inline", switch_inline_query_current_chat='') ] reply_markup = InlineKeyboardMarkup([buttons]) bot.sendMessage(uid, messages.ADD_FAVORITE, reply_markup=ForceReply(selective=True)) return ConversationHandler.END
def finish(self): # set last update self.channel.last_update = datetime.date.today() self._save_channel() new_bots = Bot.select_new_bots() if not self.silent and len(new_bots) > 0: self.notify_admin("Sending notifications to subscribers...") subscribers = Notifications.select().where( Notifications.enabled == True) notification_count = 0 for sub in subscribers: try: util.send_md_message( self.bot, sub.chat_id, messages.BOTLIST_UPDATE_NOTIFICATION.format( n_bots=len(new_bots), new_bots=Bot.get_new_bots_markdown())) notification_count += 1 sub.last_notification = datetime.date.today() sub.save() except TelegramError: pass self.sent[ 'notifications'] = "Notifications sent to {} users.".format( notification_count) changes_made = len(self.sent) > 1 or len(self.sent['category']) > 0 if changes_made: text = util.success('{}{}'.format( 'BotList updated successfully:\n\n', mdformat.results_list(self.sent))) else: text = mdformat.none_action("No changes were necessary.") log.info(self.sent) self.bot.formatter.send_or_edit(self.chat_id, text, to_edit=self.message_id)
def send_random_bot(bot, update): from botlistbot.components.explore import send_bot_details random_bot = ( Bot.select() .where( (Bot.approved == True, Bot.disabled == False), (Bot.description.is_null(False)), ) .order_by(fn.Random()) .limit(1)[0] ) send_bot_details(bot, update, random_bot)
def category_article(cat): cat_bots = Bot.of_category_without_new(cat) txt = messages.PROMOTION_MESSAGE + '\n\n' txt += "There are *{}* bots in the category *{}*:\n\n".format( len(cat_bots), str(cat)) txt += '\n'.join([str(b) for b in cat_bots]) return InlineQueryResultArticle( id=uuid4(), title=emoji.emojize(cat.emojis, use_aliases=True) + cat.name, input_message_content=InputTextMessageContent( message_text=txt, parse_mode=ParseMode.MARKDOWN), description=cat.extra, # thumb_url='https://pichoster.net/images/2017/03/13/cfa5e29e29e772373242bc177a9e5479.jpg' )
def thumbnail(username): if username[0] != '@': username = '******' + username try: item = Bot.by_username(username) except Bot.DoesNotExist: item = None if not item: return _error( "There is no bot in the BotList with the username {}.".format( username)) if not os.path.exists(item.thumbnail_file): return _error("Sorry, we don't have a thumbnail for this bot.") return send_file(item.thumbnail_file, mimetype='image/jpeg')
def forward_router(bot, update, chat_data): text = update.effective_message.text # match first username in forwarded message try: username = re.match(settings.REGEX_BOT_IN_TEXT, text).groups()[0] if username == "@" + settings.SELF_BOT_NAME: return # ignore item = Bot.get(Bot.username == username) send_bot_details(bot, update, chat_data, item) except (AttributeError, TypeError, DoesNotExist): pass # no valid username in forwarded message
def manybots(bot, update): uid = update.effective_chat.id bots = Bot.select().where( Bot.approved == True & Bot.botbuilder == True & Bot.disabled == False ) txt = "Manybots in the BotList:\n\n" # if uid in settings.MODERATORS and util.is_private_message(update): # # append admin edit buttons # txt += '\n'.join(["{} — /approve{}".format(b, b.id) for b in bots]) # else: txt += "\n".join([str(b) for b in bots]) bot.formatter.send_message(uid, txt)
def select_all(user): user_favs = list(Favorite.select().where(Favorite.user == user)) for n, f in enumerate(user_favs): try: if not fn.exists(f.bot): bot = Bot(category=Favorite.CUSTOM_CATEGORY, username=f.custom_bot, approved=True, date_added=datetime.date.today()) f.bot = bot user_favs[n] = f if not fn.exists(f.bot.category): f.bot.category = Favorite.CUSTOM_CATEGORY except (Bot.DoesNotExist, AttributeError): f.delete_instance() return user_favs
def accept_bot_submission(bot, update, of_bot: Bot, category): uid = util.uid_from_update(update) message_id = util.mid_from_update(update) user = User.from_update(update) try: of_bot.category = category of_bot.date_added = datetime.date.today() of_bot.approved = True of_bot.approved_by = user of_bot.save() buttons = [ [ InlineKeyboardButton( "Edit {} details".format(of_bot.username), callback_data=util.callback_for_action( CallbackActions.EDIT_BOT, {"id": of_bot.id} ), ) ] ] reply_markup = InlineKeyboardMarkup(buttons) bot.formatter.send_or_edit( uid, "{} has been accepted to the Botlist. ".format( of_bot ), to_edit=message_id, reply_markup=reply_markup, ) log_msg = "{} accepted by {}.".format(of_bot.username, uid) # notify submittant if of_bot.submitted_by != user: try: bot.sendMessage( of_bot.submitted_by.chat_id, util.success( messages.ACCEPTANCE_PRIVATE_MESSAGE.format( of_bot.username, of_bot.category ) ), ) log_msg += "\nUser {} was notified.".format(str(of_bot.submitted_by)) except TelegramError: log_msg += "\nUser {} could NOT be contacted/notified in private.".format( str(of_bot.submitted_by) ) log.info(log_msg) except: bot.formatter.send_failure(uid, "An error has occured. Bot not added.")
def pending_update(bot, update): uid = update.effective_chat.id bots = Bot.select_pending_update() if len(bots) == 0: update.message.reply_text("No bots pending for update.") return txt = "Bots pending for next Update:\n\n" if uid in settings.MODERATORS and util.is_private_message(update): # append admin edit buttons txt += "\n".join(["{} — /edit{}".format(b, b.id) for b in bots]) else: txt += "\n".join([str(b) for b in bots]) bot.formatter.send_message(uid, txt)
def short_approve_list(bot, update): uid = update.effective_chat.id bots = Bot.select_unapproved() if len(bots) == 0: update.message.reply_text("No bots to be approved.") return txt = "Bots pending approval:\n\n" if uid in settings.MODERATORS and util.is_private_message(update): # append admin edit buttons txt += "\n".join(["{} — /approve{}".format(b, b.id) for b in bots]) else: txt += "\n".join([str(b) for b in bots]) bot.formatter.send_message(uid, txt)
def ban_user(_bot, update, user: User, ban_state: bool): if user.banned and ban_state is True: update.message.reply_text( mdformat.none_action("User {} is already banned.".format(user)), parse_mode="markdown", ) raise DispatcherHandlerStop if not user.banned and ban_state is False: update.message.reply_text( mdformat.none_action("User {} is not banned.".format(user)), parse_mode="markdown", ) raise DispatcherHandlerStop user.banned = ban_state if ban_state is True: with db.atomic(): user_submissions = Bot.select().where( (Bot.approved == False) & (Bot.submitted_by == user) # TODO: does this need to include `Bot.deleted == True`? ) for b in user_submissions: b.delete_instance() users_suggestions = Suggestion.select().where( (Suggestion.executed == False) & (Suggestion.user == user) ) for s in users_suggestions: s.delete_instance() update.message.reply_text( mdformat.success( "User {} banned, all bot submissions and suggestions removed.".format( user ) ), parse_mode="markdown", ) Statistic.of(update, "ban", user.markdown_short) else: update.message.reply_text( mdformat.success("User {} unbanned.".format(user)), parse_mode="markdown" ) Statistic.of(update, "unban", user.markdown_short) user.save()
async def disable_decider(bot: TelegramBot, to_check: BotModel): assert to_check.disabled_reason != BotModel.DisabledReason.banned if ( to_check.offline and to_check.offline_for > settings.DISABLE_BOT_INACTIVITY_DELTA and to_check.disabled_reason != BotModel.DisabledReason.offline ): # Disable if the bot has been offline for too long if to_check.disable(to_check.DisabledReason.offline): to_check.save() if to_check.last_response: reason = "its last response was " + helpers.slang_datetime(to_check.last_response) else: reason = "it's been offline for.. like... ever" msg = "❌ {} disabled as {}.".format(to_check, reason) log.info(msg) bot.send_message(settings.BOTLIST_NOTIFICATIONS_ID, msg, timeout=30, parse_mode='markdown') else: log.info("huhwtf") elif ( to_check.online and to_check.disabled_reason == BotModel.DisabledReason.offline ): # Re-enable if the bot is disabled and came back online if to_check.enable(): to_check.save() msg = "{} was included in the @BotList again as it came back online.".format(to_check) log.info(msg) bot.send_message(settings.BOTLIST_NOTIFICATIONS_ID, msg, timeout=30, parse_mode='markdown') else: log.info("huhwtf")
def num_contributions(self): from botlistbot.models import Bot return Bot.select().where(Bot.submitted_by == self).count()