class CommandHandler(object): def __init__(self, client): self.client = client self.guild = None self.channel = None self.author = None self.message_content = None self.staff_role = None self.loop = asyncio.get_event_loop() self.filter_service = FilterService() self.file_service = FileService() self.discord_service = DiscordService(self.client) self.raffle_service = RaffleService() self.util_service = UtilService() self.verification_service = VerificationService(client) self.settings = self.file_service.load_json_file( "resources/settings.json") self.filters = self.file_service.load_json_file( "resources/filters.json") self.responses = self.file_service.load_json_file( "resources/responses.json") self.command_texts = self.file_service.load_json_file( "resources/command_texts.json") async def handle(self, message, client): self.guild = message.guild self.channel = message.channel self.author = message.author self.message_content = message.content self.client = client self.staff_role = self.discord_service.get_role_id( self.settings["guild_name"], self.settings["staff_role"]) self.banned_role = self.discord_service.get_role_id( self.settings["guild_name"], self.settings["banned_role"]) self.verified_role = self.discord_service.get_role_id( self.settings["guild_name"], self.settings["verified_role"]) if (self.guild is not None) and self.banned_role in self.author.roles: return if self.message_content.startswith(self.settings["prefix"]): commands = self.message_content[1:].split() command = "_cmd_{0}".format(commands[0].lower()) if hasattr(self, command): return getattr(self, command)(commands[1:]) else: if any(word in self.filters["filters"] for word in self.message_content.lower().split()): self.discord_service.moderate_user(message) elif any(word in self.filters["suspicious"] for word in self.message_content.lower().split()): self.discord_service.inform_suspicious_message( self.author, self.channel, self.message_content) elif "discord.gg" in self.message_content: await self.discord_service.check_for_invites(message) elif any(keyword in self.message_content.lower() for keyword in self.responses.keys()): self.discord_service.get_response_answer(message) return None def _cmd_addfilter(self, args): if len(args) == 1 and self.staff_role in self.author.roles: keyword = args[0] result = self.filter_service.add_filter(keyword) if result: self.filters["filters"].append(keyword) return self.command_texts["addFilterResult" + str(result)].format(keyword) else: return self.get_wrong_command_usage("badAddFilterCommand") def _cmd_removefilter(self, args): if len(args) == 1 and self.staff_role in self.author.roles: keyword = args[0] result = self.filter_service.remove_filter(keyword) if result: self.filters["filters"].remove(keyword) return self.command_texts["removeFilterResult" + str(result)].format(keyword) else: return self.get_wrong_command_usage("badRemoveFilterCommand") def _cmd_showfilter(self, args): if len(self.filter_service.filters["filters"] ) > 0 and self.staff_role in self.author.roles: return ", ".join(self.filter_service.filters["filters"]) else: return self.command_texts["emptyFilter"] def _cmd_lsfirstmembers(self, args): if self.staff_role in self.author.roles: return "\n".join( self.discord_service.list_first_members(self.guild)) def _cmd_flipcoin(self, args): if self.verified_role in self.author.roles: self.discord_service.flip_coin(self.channel) return None def _cmd_rafflemode(self, args): if self.staff_role in self.author.roles: return self.command_texts[ "raffleMode" + str(self.raffle_service.toggle_raffle_mode())] def _cmd_kelkodver(self, args): if self.verified_role in self.author.roles: result = self.raffle_service.add_participant(self.author.mention) if result: return self.command_texts["addedNewRaffleParticipant"].format( self.author.mention) else: return self.command_texts["participantAlreadyAdded"].format( self.author.mention) def _cmd_lsparticipants(self, args): if self.staff_role in self.author.roles: result = self.raffle_service.list_participants() if len(result) == 0: return self.command_texts["noParticipants"] return ", ".join(self.raffle_service.list_participants()) def _cmd_pickwinner(self, args): if self.staff_role in self.author.roles: result = self.raffle_service.pick_winner() if result: return self.command_texts["raffleWinner"].format(result) else: return self.command_texts["noWinners"] def _cmd_random(self, args): if self.verified_role in self.author.roles: return self.util_service.create_random_laugh() def _cmd_purge(self, args): if self.staff_role in self.author.roles: if len(args) == 1 and args[0].isnumeric(): no_of_msgs_to_delete = int(args[0]) self.discord_service.purge_messages(self.channel, no_of_msgs_to_delete, self.message_content, self.author) return None else: return self.get_wrong_command_usage("badPurgeCommand") def _cmd_msg(self, args): if len(args[0]) > 1 and self.staff_role in self.author.roles: self.discord_service.create_bot_message(args, self.author) return None else: return self.get_wrong_command_usage("badMsgCommand") def _cmd_riotduyuru(self, args): if self.verified_role in self.author.roles: asyncio.get_event_loop().create_task( self.discord_service.give_role( self.guild, self.author, self.settings["board_announcements_role"])) return self.command_texts["addedAnnouncementsRole"].format( self.settings["board_announcements_channel"]) def _cmd_destek(self, args): return "https://support.riotgames.com/hc/tr" def _cmd_doan(self, args): if self.verified_role in self.author.roles: return "gel vs, tüm güçle alırım seni! item al gel full para dimi gel! - doğan, 2019 https://www.youtube.com/watch?v=eoUn5toyZvY" def _cmd_onayyardim(self, args): if self.staff_role in self.author.roles: return "\n\n".join([ self.command_texts["verificationSteps"], self.command_texts["verificationPossibleError"] ]) def _cmd_verify(self, args): if len(args) == 0: return self.get_wrong_command_usage("badVerificationCommand") summoner_name = " ".join(args) self.loop.create_task( self.verification_service.wait_and_verify(self.guild, self.author, summoner_name)) def get_wrong_command_usage(self, which): return self.command_texts["correctUsage"] + self.command_texts[which]
class FilterService(object): def __init__(self): self.file_service = FileService() self.filters = self.file_service.load_json_file( "resources/filters.json") def add_filter(self, keyword): if keyword in self.filters["filters"]: return False self.filters["filters"].append(keyword) self.file_service.save_json_file("resources/filters.json", self.filters) return True def remove_filter(self, keyword): try: self.filters["filters"].remove(keyword) self.file_service.save_json_file("resources/filters.json", self.filters) return True except ValueError: return False
class LoLBoardsGetter(object): def __init__(self): self.file_service = FileService() self.link = "https://boards.tr.leagueoflegends.com/tr/?sort_type=recent" self.raw_data = "failed" self.json_data = self.get_checked_threads() self.checked_thread_ids = self.json_data["checkedThreads"] def get_data(self): try: return requests.get(self.link, timeout=5).text except requests.RequestException: return "failed" def get_checked_threads(self): return self.file_service.load_json_file( "resources/checked_threads.json") def set_checked_threads(self): self.file_service.save_json_file("resources/checked_threads.json", self.json_data) def check_new_posts(self): self.raw_data = self.get_data() if self.raw_data != "failed": thread_list = [] data = BeautifulSoup(self.raw_data, "html.parser") discussions = data.find("tbody", {"id": "discussion-list"}) threads = discussions.find_all( "tr", {"class": "discussion-list-item row"}) for thread in threads: parsed_data = thread.find_all("td") if len(parsed_data) == 5: votes, title_html, riot_commented, number_of_comments, views = parsed_data else: votes, thumbnail, title_html, riot_commented, number_of_comments, views = parsed_data spans = title_html.find_all("span") username = "******" realm = "(unknown)" author_url = "https://boards.tr.leagueoflegends.com/tr/player/TR/{0}" author_icon = "https://avatar.leagueoflegends.com/tr/{0}.png" if len(spans) == 2: # ANNOUNCEMENT title_span, time_span = spans username = "******" realm = "(TR)" author_url = "https://tr.leagueoflegends.com/tr/news/" author_icon = "https://www.riotgames.com/darkroom/original/06fc475276478d31c559355fa475888c:af22b5d4c9014d23b550ea646eb9dcaf/riot-logo-fist-only.png" elif len(spans) == 4: # NORMAL THREAD title_span, username_span, realm_spam, time_span = spans username = username_span.text realm = realm_spam.text author_icon = author_icon.format(username) author_url = author_url.format(username) elif len(spans) == 6: # NORMAL THREAD WITH URL title_span, icon_url, icon_info, username_span, realm_spam, time_span = spans username = username_span.text realm = realm_spam.text author_icon = author_icon.format(username) author_url = author_url.format(username) board2 = title_html.find( "div", {"class": "discussion-footer byline opaque"}) board = board2.find_all("a") thread_id = votes.div["data-apollo-discussion-id"] thread_url = "https://boards.tr.leagueoflegends.com/%s" % title_html.a[ "href"] title = title_span.text post = title_span["title"] post_time = time_span["title"] if len(board) == 1: board = board[0].text else: board = board[1].text post = post.replace("ü", "ü").replace("Ü", "Ü").replace("ç", "ç")\ .replace("Ç", "Ç") if thread_id in self.checked_thread_ids: continue thread_info = { "thread_id": thread_id, "thread_url": thread_url, "thread_title": title, "post": post, "post_time": post_time, "username": username, "realm": realm, "board": board, "author_icon": author_icon, "author_url": author_url } thread_list.append(thread_info) self.json_data["checkedThreads"].append(thread_id) self.set_checked_threads() return thread_list else: return []
class DiscordBot(discord.Client): def __init__(self, factory): super().__init__() self.factory = factory self.loop = asyncio.get_event_loop() self.file_service = FileService() self.discord_service = DiscordService(self) self.settings = self.file_service.load_json_file( "resources/settings.json") self.lol_data = self.file_service.load_json_file( "resources/lol_data.json") self.command_texts = self.file_service.load_json_file( "resources/command_texts.json") self.lol_boards = LoLBoardsGetter() self.command_handler = CommandHandler(self) self.raffle_mode = False self.tasks = {} async def begin(self): await self.start(self.settings["token"]) async def on_ready(self): logging.info("Connected as: {0}".format(self.user.name)) self.tasks["lol boards task"] = asyncio.Task(self.check_lol_boards()) async def on_member_join(self, member): nick = self.command_texts["newUsernameNickname"].format( random.choice(self.lol_data["champions"])) welcome_message = self.command_texts["welcomeMessage"].format( member.mention) channel = self.discord_service.get_channel_id( self.settings["guild_name"], self.settings["welcome_channel"]) await channel.send(welcome_message) await member.edit(nick=nick) async def on_message_delete(self, message): message_time = message.created_at + timedelta(hours=3) pyear = message_time.strftime("%d/%m/%Y") ptime = message_time.strftime("%H:%M:%S") text = "{0} - {1}: {2}".format(message.channel.mention, message.author.mention, message.content) embed = self.discord_service.create_embedded_deleted_message( message.author, text, pyear, ptime) channel = self.discord_service.get_channel_id( self.settings["guild_name"], self.settings["deleted_messages_channel"]) await channel.send(embed=embed) async def on_message(self, message): channel = message.channel author = message.author message_content = message.content guild = message.guild logging.info("[{0}][{1}][{2}] {3}".format(guild, channel, author, message_content)) if author.name == self.user.name: return else: result = await self.command_handler.handle(message, self) if result: for i in range((int(len(result) / 2000)) + 1): # If we get a response longer than 2000 characters, split it into different messages await channel.send(result[i * 2000:(i * 2000) + 2000]) def check_lol_boards(self): while True: threads = self.lol_boards.check_new_posts() for thread in reversed(threads): post_time = datetime.strptime( thread["post_time"][:-5], "%Y-%m-%dT%H:%M:%S.%f") + timedelta(hours=3) pyear = post_time.strftime("%d/%m/%Y") ptime = post_time.strftime("%H:%M:%S") embed = self.discord_service.create_embedded_board_message( thread["username"], thread["realm"], thread["board"], thread["thread_title"], thread["thread_url"], thread["post"], thread["author_url"], thread["author_icon"], pyear, ptime) if thread["board"] == "Duyurular": channel = self.discord_service.get_channel_id( self.settings["guild_name"], self.settings["board_announcements_channel"]) announcement_role = self.discord_service.get_role_id( self.settings["guild_name"], self.settings["board_announcements_role"]) self.loop.create_task( channel.send(announcement_role.mention, embed=embed)) else: channel = self.discord_service.get_channel_id( self.settings["guild_name"], self.settings["board_channel"]) self.loop.create_task(channel.send(embed=embed)) yield from asyncio.sleep(15)
class VerificationService(object): def __init__(self, client): self.characters = string.ascii_uppercase + string.digits self.file_service = FileService() self.discord_service = DiscordService(client) self.client = client self.command_texts = self.file_service.load_json_file( "resources/command_texts.json") self.settings = self.file_service.load_json_file( "resources/settings.json") self.verify_pending = [] cass.set_riot_api_key(self.settings["riot_api_key"]) cass.set_default_region("TR") def random_generator(self): return "".join(random.choice(self.characters) for _ in range(5)) def generate_code(self): return "-".join([self.random_generator() for _ in range(5)]) async def check_verification(self, guild, author, summoner_name, expected_verification): try: summoner = Summoner(name=summoner_name) verification_string = summoner.verification_string summoner_name = summoner.name if (expected_verification == verification_string): await author.send(self.command_texts["correctVerification"]) await self.discord_service.change_member_nickname( author, summoner_name) await self.discord_service.give_role( guild, author, self.settings["verified_role"]) self.verify_pending.remove(author) else: wrong_text = self.command_texts["wrongVerification"].format( verification_string, expected_verification) await author.send(wrong_text) self.verify_pending.remove(author) await self.wait_and_verify(guild, author, summoner_name, expected_verification) except APIRequestError as e: await author.send(str(e)) self.verify_pending.remove(author) except Exception as e: await author.send( self.command_texts["noVerification"].format(summoner_name)) self.verify_pending.remove(author) await self.wait_and_verify(author, author, summoner_name, expected_verification) async def wait_and_verify(self, guild, author, summoner_name, expected_verification=None): if author in self.verify_pending: await author.send(self.command_texts["verificationAlreadyPending"]) return self.verify_pending.append(author) if expected_verification is None: expected_verification = self.generate_code() verification_message = self.discord_service.create_embedded_verification( summoner_name, expected_verification) verification_help = "\n\n".join([ self.command_texts["verificationSteps"], self.command_texts["verificationPossibleError"] ]) await author.send(verification_help, embed=verification_message) def check(message): verified = "!onayladım" cancelled = "!iptal" return message.content in [verified, cancelled ] and message.author == author try: msg = await self.client.wait_for('message', check=check, timeout=300.0) if msg.content == "!iptal": await author.send(self.command_texts["verificationCancelled"]) self.verify_pending.remove(author) elif msg.content == "!onayladım": await self.check_verification(guild, author, summoner_name, expected_verification) except asyncio.TimeoutError: await author.send( self.command_texts["verificationTimeout"].format(summoner_name) ) self.verify_pending.remove(author)
class DiscordService(object): def __init__(self, client): super().__init__() self.file_service = FileService() self.settings = self.file_service.load_json_file( "resources/settings.json") self.command_texts = self.file_service.load_json_file( "resources/command_texts.json") self.responses = self.file_service.load_json_file( "resources/responses.json") self.filters = self.file_service.load_json_file( "resources/filters.json") self.client = client def list_first_members(self, guild): memberstime = {} for member in guild.members: if member.nick: memberstime[member.nick] = member.joined_at else: memberstime[member.name] = member.joined_at members = sorted(memberstime.items(), key=operator.itemgetter(1)) lst_members = [ "%s - %s" % (name, time.strftime("%d/%m/%Y - %H:%M:%S")) for name, time in members ] for i, member in enumerate(lst_members): lst_members[i] = "%s. %s" % (i + 1, lst_members[i]) return lst_members def create_bot_message(self, args, user): chan = self.client.get_channel(int(args[0])) msg = " ".join(args[1:]) asyncio.get_event_loop().create_task(chan.send(msg)) embed = self.create_embedded_action_logs("!msg " + " ".join(args), user) logs_channel = self.get_channel_id(self.settings["guild_name"], self.settings["mod_logs_channel"]) asyncio.get_event_loop().create_task(logs_channel.send(embed=embed)) def purge_messages(self, channel, number_of_messages, cmd, user): try: asyncio.get_event_loop().create_task( channel.purge(limit=number_of_messages)) embed = self.create_embedded_action_logs(cmd, user) logs_channel = self.get_channel_id( self.settings["guild_name"], self.settings["mod_logs_channel"]) asyncio.get_event_loop().create_task( logs_channel.send(embed=embed)) except Exception: pass def moderate_user(self, message): date = datetime.now() + timedelta(hours=3) post_year = date.strftime("%d/%m/%Y") post_time = date.strftime("%H:%M:%S") warning_text = "{0} {1}".format( message.author.mention, random.choice(self.filters["filter_warns"])) sanctions_channel = self.get_channel_id( self.settings["guild_name"], self.settings["sanctions_channel"]) moderated_text = "{0} - {1}: {2}".format(message.channel.mention, message.author.mention, message.content) embed = self.create_embedded_moderated_message(moderated_text, message.author, post_year, post_time) role = [ discord.utils.find( lambda r: r.name == self.settings["banned_role"], message.guild.roles) ] # delete the message asyncio.get_event_loop().create_task(message.delete()) # warn the user asyncio.get_event_loop().create_task( message.channel.send(warning_text)) # send the message to sanctions channel asyncio.get_event_loop().create_task( sanctions_channel.send(embed=embed)) # change the roles to banned asyncio.get_event_loop().create_task(message.author.edit(roles=role)) async def change_member_nickname(self, member, new_nick): await member.edit(nick=new_nick) async def check_for_invites(self, message): bot_guild = self.get_guild_id(self.settings["guild_name"]) active_invites = await bot_guild.invites() texts = message.content.split() for text in texts: try: invite = await self.client.fetch_invite(text) if invite not in active_invites: self.moderate_user(message) except Exception: pass async def give_role(self, guild, member, role_name): role = self.get_role_id(guild.name, role_name) await member.add_roles(role) def inform_suspicious_message(self, user, channel, message_content): text = "Uygunsuz mesaj şüphesi:\n\n %s - %s: %s" % ( user.mention, channel.mention, message_content) staff_channel = self.get_channel_id(self.settings["guild_name"], self.settings["staff_channel"]) asyncio.get_event_loop().create_task(staff_channel.send(text)) def get_response_answer(self, message): k = "" for keyword in self.responses.keys(): if keyword in message.content.lower(): k = keyword break text = "{0} {1}".format(message.author.mention, random.choice(self.responses[k])) asyncio.get_event_loop().create_task(message.channel.send(text)) def create_embedded_board_message(self, username, realm, board, thread_title, thread_url, post, author_url, author_icon, post_year, post_time): thread_url = thread_url.replace(" ", "%20") author_icon = author_icon.replace(" ", "%20") author_url = author_url.replace(" ", "%20") embed = discord.Embed( title="Konu adı: {0}".format(thread_title), url=thread_url, description="{0} panosunda açıldı.".format(board)) embed.set_author(name="{} {} tarafından yeni konu açıldı!".format( username, realm), url=author_url, icon_url=author_icon) embed.set_thumbnail( url= "https://lolstatic-a.akamaihd.net/apollo/assets/vb/boards-wallpaper.jpg" ) embed.add_field(name="İçerik:", value=post, inline=False) embed.set_footer( text="Tarih: {0} Saat: {1}".format(post_year, post_time)) return embed def create_embedded_deleted_message(self, user, message, post_year, post_time): embed = discord.Embed(title="💬❌ Silinen mesaj tespit edildi", description=message) embed.set_author(name=user.name, icon_url=user.avatar_url) embed.set_footer( text="Tarih: {0} Saat: {1}".format(post_year, post_time)) return embed def create_embedded_moderated_message(self, message, user, post_year, post_time): embed = discord.Embed(title="⛔ Bir ceza verildi", description=message) embed.set_author(name=user.name, icon_url=user.avatar_url) embed.set_footer( text="Tarih: {0} Saat: {1}".format(post_year, post_time)) return embed def create_embedded_action_logs(self, message, user): date = datetime.now() + timedelta(hours=3) post_year = date.strftime("%d/%m/%Y") post_time = date.strftime("%H:%M:%S") embed = discord.Embed(title="❗ Moderasyon komutu kullanıldı", description=message) embed.set_author(name=user.name, icon_url=user.avatar_url) embed.set_footer( text="Tarih: {0} Saat: {1}".format(post_year, post_time)) return embed def create_embedded_verification(self, username, key): embed = discord.Embed() embed.add_field(name="Sihirdar Adı", value=username, inline=True) embed.add_field(name="Harici Onaylama Kodu", value=key, inline=True) embed.set_image( url= "https://cdn.discordapp.com/attachments/381812662088105986/622183230166269963/verification.gif" ) embed.set_footer( text=self.settings["guild_name"] + " verification system isn't endorsed by Riot Games and doesn't reflect the views or opinions of Riot Games or anyone officially involved in producing or managing League of Legends. League of Legends and Riot Games are trademarks or registered trademarks of Riot Games, Inc. League of Legends © Riot Games, Inc." ) return embed def get_channel_id(self, guild_name, channel_name): return discord.utils.get(self.client.get_all_channels(), guild__name=guild_name, name=channel_name) def get_role_id(self, guild_name, role_name): guild = self.get_guild_id(guild_name) return discord.utils.find(lambda r: r.name == role_name, guild.roles) def get_guild_id(self, guild_name): return discord.utils.find(lambda s: s.name == guild_name, self.client.guilds) def get_member(self, member_id, guild): return discord.utils.find(lambda m: m.id == member_id, guild.members) def flip_coin(self, channel): asyncio.get_event_loop().create_task( channel.send(self.command_texts["flippingCoin"])) async def flipcoin(): await asyncio.sleep(2) await channel.send(random.choice(["Yazı!", "Tura!"])) asyncio.get_event_loop().create_task(flipcoin()) async def check_verification(self, channel, author): def check_verification(message): verified = "!onayladım" cancelled = "!iptal" return message.content in [verified, cancelled ] and message.author == author try: msg = await self.client.wait_for('message', check=check_verification, timeout=5.0) if msg == "!iptal": await channel.send(self.command_texts["verificationCancelled"]) elif msg == "!onayladım": return 1 except asyncio.TimeoutError: await channel.send(self.command_texts["verificationTimeout"])