async def change_status(self, name): log.debug("Changing status to {}".format(name)) log_to_file("Changing status to {}".format(name)) shards = list(self.client.shards.keys()) for shard_id in shards: customized = name + " | shard {}".format(shard_id + 1) await bot.change_presence(activity=discord.Activity(type=discord.ActivityType.watching, name="you"))
async def change_status(self, name): log.debug("Changing status to {}".format(name)) log_to_file("Changing status to {}".format(name)) shards = list(self.client.shards.keys()) for shard_id in shards: customized = name + " | shard {}".format(shard_id + 1) await self.client.change_presence(game=Game(name=customized, type=0), shard_id=shard_id)
async def start(): if not parser.has_option("Credentials", "token"): log.critical("Token not found. Check your settings.ini") log_to_file("Could not start: Token not specified") return token = parser.get("Credentials", "token") await client.login(token) await client.connect()
async def random_cat(self, type_="gif"): # structure: # response -> data -> images -> image -> url try: data = await self.req.get_html(self.url, api_key=self.key, format=self.format, size=self.size, type=type_) link = BeautifulSoup(data, "lxml").find("img").get("src") except (APIFailure, Exception): log_to_file("CatGenerator exception\n{}".format(traceback.format_exc()), "bug") return None return link
async def on_guild_join(self, guild, **kwargs): # Always 'en' lang = kwargs.get("lang") d_chan = await self.default_channel(guild) # Say hi to the server await self.send_message_failproof( d_chan, self.trans.get("EVENT_SERVER_JOIN", lang)) # Create server settings self.handler.server_setup(guild) # Log log_to_file("Joined guild: {}".format(guild.name))
async def on_ready(): # Just prints "Resumed connection" if that's the way it is global IS_RESUME if IS_RESUME: print("Resumed connection...") log_to_file("Resumed connection as {}".format(client.user.name)) return IS_RESUME = True print("connected!") print("BOT name: {} ({})".format(client.user.name, client.user.id)) log_to_file("Connected as {} ({})".format(client.user.name, client.user.id)) await nano.dispatch_event(ON_READY)
async def start(self): while self.enabled: # Run the backup every day or as specified await sleep(self.time) # Full backup counter self.temp_keep -= 1 if self.temp_keep <= 0: dated_backup = True self.temp_keep = int(self.keep_every) else: dated_backup = False log_to_file("Creating a backup...") self.backup(dated_backup)
async def run(self): await self.client.wait_until_ready() # Shuffle the game list in place shuffle(game_list) for game in game_list: await self.change_status(game) await sleep(self.time) if self.client.is_closed: break # Shuffle when the whole list is used shuffle(game_list) log_to_file("Exited status changer")
def main(): try: print("Connecting to Discord...", end="") loop.run_until_complete(start()) except Exception as e: loop.run_until_complete(client.logout()) log.critical("Something went wrong, quitting (see log bugs.txt)") log_to_file("CRITICAL, shutting down: {}".format(e), "bug") # Attempts to save plugin state log.critical("Dispatching ON_SHUTDOW...") loop.run_until_complete(nano.dispatch_event(ON_SHUTDOWN)) log.critical("Shutting down...") finally: loop.close()
async def monitor(self): await self.client.wait_until_ready() last_time = time.time() while True: # Iterate through users and their reminders a = self.get_all_reminders() for user in a: for id_, reminder in user.items(): # If enough time has passed, send the reminder if int(reminder["time_target"]) <= last_time: try: await self.dispatch(reminder) except (DiscordException, KeyError): log.warning("ERROR in reminders, see bugs.txt") log_to_file(traceback.format_exc(), "bug") self.remove_reminder(reminder["author"], id_) # And tick. last_time = await self.tick(last_time)
async def on_message(self, message, **kwargs): client = self.client prefix = kwargs.get("prefix") lang = kwargs.get("lang") # Check if this is a valid command if not is_valid_command(message.content, commands, prefix): return else: self.stats.add(MESSAGE) def startswith(*matches): for match in matches: if message.content.startswith(match): return True return False # Global owner filter if not self.handler.is_bot_owner(message.author.id): await message.channel.send(self.trans.get("PERM_OWNER", lang)) return # nano.dev.server_info [id] elif startswith("nano.dev.server_info"): s_id = message.content[len("nano.dev.server_info "):] srv = utils.find(lambda b: b.id == s_id, client.guilds) if not srv: await message.channel.send("No such guild. " + StandardEmoji.CROSS) return nano_data = self.handler.get_server_data(srv) to_send = "{}\n```css\nMember count: {}\nChannels: {}\nOwner: {}```\n" \ "*Settings*: ```{}```".format(srv.name, srv.member_count, ",".join([ch.name for ch in srv.channels]), srv.owner.name, nano_data) await message.channel.send(to_send) # nano.dev.test_exception elif startswith("nano.dev.test_exception"): int("abcdef") # nano.dev.embed_test elif startswith("nano.dev.embed_test"): emb = Embed(title="Stats", colour=Colour.darker_grey()) emb.add_field(name="Messages Sent", value="sample messages") await message.channel.send("Stats", embed=emb) # nano.dev.backup elif startswith("nano.dev.backup"): self.backup.manual_backup() await message.channel.send("Backup completed " + StandardEmoji.PERFECT) # nano.dev.leave_server elif startswith("nano.dev.leave_server"): try: sid = int(message.content[len("nano.dev.leave_server "):]) except ValueError: await message.channel.send("Not a number.") return srv = await self.client.get_guild(sid) await srv.leave() await message.channel.send("Left {}".format(srv.id)) # nano.dev.tf.reload elif startswith("nano.dev.tf.clean"): self.nano.get_plugin("tf2").instance.tf.request() await message.channel.send("Re-downloaded data...") # nano.dev.plugin.reload elif startswith("nano.dev.plugin.reload"): name = message.content[len("nano.dev.plugin.reload "):] v_old = self.nano.get_plugin(name).plugin.NanoPlugin.version s = await self.nano.reload_plugin(name) v_new = self.nano.get_plugin(name).plugin.NanoPlugin.version if s: await message.channel.send("Successfully reloaded **{}**\nFrom version *{}* to *{}*.".format(name, v_old, v_new)) else: await message.channel.send("Something went wrong, check the logs.") # nano.dev.servers.clean elif startswith("nano.dev.servers.tidy"): self.handler.delete_server_by_list([s.id for s in self.client.guilds]) # nano.restart elif startswith("nano.restart"): await message.channel.send("**DED, but gonna come back**") await client.logout() self.shutdown_mode = "restart" return "shutdown" # nano.kill elif startswith("nano.kill"): await message.channel.send("**DED**") await client.logout() self.shutdown_mode = "exit" return "shutdown" # nano.playing elif startswith("nano.playing"): status = message.content[len("nano.playing "):] await client.change_presence(activity=Game(name=str(status))) await message.channel.send("Status changed " + StandardEmoji.THUMBS_UP) # nano.dev.translations.reload elif startswith("nano.dev.translations.reload"): self.trans.reload_translations() await message.channel.send(StandardEmoji.PERFECT) # nano.dev.test_default_channel elif startswith("nano.dev.test_default_channel"): df = await self.default_channel(message.guild) if not df: await message.channel.send("No default channel? w a t") return await message.channel.send("Default channel is {}, sending test message".format(df.mention)) await df.send("This is a test message. Apparently everything is ok.") # nano.dev.announce elif startswith("nano.dev.announce"): await message.channel.send("Sending... ") ann = message.content[len("nano.dev.announce "):] s = [] for g in self.client.guilds: try: d_chan = await self.default_channel(g) await d_chan.send(ann) log_to_file("Sent announcement for {}".format(g.name)) s.append(g.name) except DiscordException: log_to_file("Couldn't send announcement for {}".format(g.name)) await message.channel.send("Sent to {} servers".format(len(s))) # nano.dev.userdetective elif startswith("nano.dev.userdetective"): param = str(message.content[len(prefix + "nano.dev.userdetective"):]) # Number if param.isdigit(): user = self.client.get_user(int(param)) if not user: await message.channel.send("No user with such ID.") return elif len(message.mentions) > 0: user = message.mentions[0] else: members = [user for user in self.client.get_all_members() if user.name == param] if not members: await message.channel.send("No users with that name.") return user = members[0] srv_in_common = 0 server_table_temp = [] # Loop though servers and find ones the user is in for srv in self.client.guilds: mem = srv.get_member(user.id) if mem: srv_in_common += 1 server_table_temp.append("{}: {}".format(srv.name, mem.display_name)) join_time_ago = int((datetime.now() - user.created_at).total_seconds()) join_time_ago = resolve_time(join_time_ago, "en") embed = Embed(title="{}#{}{}".format(user.name, user.discriminator, ":robot:" if user.bot else ""), description="ID: {}".format(user.id)) embed.add_field(name="Joined Discord", value="**{}** ago\nISO time: {}".format(join_time_ago, user.created_at)) embed.add_field(name="Avatar url", value=user.avatar_url_as(format="png")) embed.add_field(name="Servers in common", value="**{}** on this shard:\n```http\n{}```".format(srv_in_common, "\n".join(server_table_temp))) await message.channel.send(embed=embed)
async def on_guild_remove(self, guild, **_): # Deletes server data self.handler.delete_server(guild.id) # Log log_to_file("Removed from guild: {}".format(guild.name))
async def on_message(self, message, **kwargs): trans = self.trans prefix = kwargs.get("prefix") lang = kwargs.get("lang") # Check if this is a valid command if not is_valid_command(message.content, commands, prefix): return else: self.stats.add(MESSAGE) def startswith(*matches): for match in matches: if message.content.startswith(match): return True return False # !poll start # Arguments: "[title]" [option1]|(option2)|... # OR : "[title]" [option1],(option2),... if startswith(prefix + "poll start"): if not self.handler.is_admin(message.author, message.guild): await message.channel.send(trans.get("PERM_ADMIN", lang)) self.stats.add(WRONG_PERMS) return if self.vote.in_progress(message.guild.id): await message.channel.send( trans.get("MSG_VOTING_IN_PROGRESS", lang)) return arguments = message.content[len(prefix + "poll start "):].strip(" ") if not arguments: await message.channel.send( trans.get("MSG_VOTING_I_USAGE", lang).format(prefix)) return # TITLE # Handle short_title, "longer title", 'also like this' if arguments[0] == "\"": _, title, arguments = arguments.split("\"", maxsplit=3) elif arguments[0] == "'": _, title, arguments = arguments.split("'", maxsplit=3) else: title, arguments = arguments.split(" ", maxsplit=1) arguments = arguments.lstrip(" ") # CHOICES # | as separator if "|" in arguments: items = [a.strip(" ") for a in arguments.split("|") if a] # , used as separator else: items = [a.strip(" ") for a in arguments.split(",") if a] # Send an error if there's only a title if len(items) < 2: await message.channel.send( trans.get("MSG_VOTING_NEED_OPTIONS", lang).format(prefix)) return # END OF ARGUMENT PARSING # Check item amount if len(items) > VOTE_ITEM_LIMIT: await message.channel.send( trans.get("MSG_VOTING_OPTIONS_TM", lang).format(VOTE_ITEM_LIMIT, len(items))) return # Check total length if (len(title) + sum([len(a) for a in items])) > VOTE_ITEM_MAX_LENGTH: await message.channel.send( trans.get("MSG_VOTING_OPTIONS_TL ", lang).format(VOTE_ITEM_MAX_LENGTH, sum([len(a) for a in items]))) return # Check if any option is empty if any(e == "" for e in items): await message.channel.send( trans.get("MSG_VOTING_EMPTY_ITEM", lang)) return self.vote.start_vote(message.author.id, message.guild.id, title, items) # Generates a list of options to show choices = "\n\n".join([ "[{}]\n{}".format(en + 1, ch) for en, ch in enumerate( self.vote.get_choices(message.guild.id)) ]) await message.channel.send( trans.get("MSG_VOTING_STARTED", lang).format(title, choices)) # !poll end elif startswith(prefix + "poll end"): if not self.handler.is_admin(message.author, message.guild): await message.channel.send(trans.get("PERM_ADMIN", lang)) self.stats.add(WRONG_PERMS) return if not self.vote.in_progress(message.guild.id): await message.channel.send( trans.get("MSG_VOTING_NO_PROGRESS", lang)) return # Wait for confirmation msg = await message.channel.send( trans.get("MSG_VOTING_END_CONFIRMATION", lang).format(OK_EMOJI)) await msg.add_reaction(OK_EMOJI) def check(reaction, user): return user == message.author and str( reaction.emoji) == OK_EMOJI try: await self.client.wait_for('reaction_add', timeout=45, check=check) except asyncio.TimeoutError: await message.channel.send( trans.get("MSG_VOTING_END_ABORT", lang)) return await msg.delete() votes = self.vote.get_votes(message.guild.id) title = self.vote.get_title(message.guild.id) total_votes = sum(votes.values()) embed = Embed(title="**{}**".format(title), colour=Colour(0x303F9F), description=trans.get("MSG_VOTING_AMOUNT", lang).format(total_votes)) for name, val in votes.items(): # Zero-width space dotted = add_dots(name, max_len=240) or "\u200B" embed.add_field(name=dotted, value=trans.get("MSG_VOTING_AMOUNT2", lang).format(val)) # Actually end the voting self.vote.end_voting(message.guild.id) try: await message.channel.send( trans.get("MSG_VOTING_ENDED", lang) + "\n", embed=embed) except errors.HTTPException as e: await message.channel.send(trans.get("MSG_VOTING_ERROR", lang)) log_to_file("VOTING ({}): {}".format(e, embed.to_dict()), "bug") # !poll status elif startswith(prefix + "poll status"): if not self.vote.in_progress(message.guild.id): await message.channel.send( trans.get("MSG_VOTING_NO_PROGRESS", lang)) return header = self.vote.get_title(message.guild.id) votes = sum(self.vote.get_votes(message.guild.id).values()) if votes == 0: vote_disp = trans.get("MSG_VOTING_S_NONE", lang) elif votes == 1: vote_disp = trans.get("MSG_VOTING_S_ONE", lang) else: vote_disp = trans.get("MSG_VOTING_S_MULTI", lang).format(votes) await message.channel.send( trans.get("MSG_VOTING_STATUS", lang).format(header, vote_disp)) # !vote elif startswith(prefix + "vote"): # Ignore if there is no vote going on instead of getting an exception if not self.vote.in_progress(message.guild.id): await message.add_reaction(X_EMOJI) msg = await message.channel.send( trans.get("MSG_VOTING_NO_PROGRESS", lang)) await asyncio.sleep(2) await msg.delete() return # Get the choice, but tell the author if he/she didn't supply a number try: choice = int(message.content[len(prefix + "vote "):]) - 1 # Cannot convert to int except ValueError: await message.add_reaction(BLOCK_EMOJI) m = await message.channel.send( trans.get("MSG_VOTING_NOT_NUMBER", lang)) await asyncio.sleep(2) await m.delete() return res = self.vote.plus_one(choice, message.author.id, message.guild.id) # User already voted if res == -1: await message.add_reaction(BLOCK_EMOJI) msg = await message.channel.send( trans.get("MSG_VOTING_CHEATER", lang)) await asyncio.sleep(2) await msg.delete() # No such option elif not res: await message.add_reaction(X_EMOJI) msg = await message.channel.send( trans.get("MSG_VOTING_INVALID_NUMBER", lang)) await asyncio.sleep(2) await msg.delete() # Everything ok, was added else: await message.add_reaction(OK_EMOJI) self.stats.add(VOTE)
async def on_error(event, *args, **kwargs): e_type, value, _ = sys.exc_info() # Ignore Forbidden errors (but log them anyways) if e_type == errors.Forbidden: log.warning("Forbidden 403") if isinstance(args[0], Message): log_to_file("Forbidden 403. Server: {}, channel: {}".format(args[0].guild, args[0].channel)) elif isinstance(args[0], Member): log_to_file("Forbidden 403. Server: {}, member: {}:{}".format(args[0].guild, args[0].name, args[0].id)) else: try: items = args[0].__dict__ except AttributeError: items = args[0].__slots__ log_to_file("Forbidden 403. Unknown instance: {}:{}".format(type(args[0]), items)) elif e_type == errors.HTTPException and str(value).startswith("BAD REQUEST"): log.warning("Bad Request 400") log_to_file("Bad Request 400: \nTraceback: {}".format(kwargs), "bug") elif e_type == errors.NotFound: log.warning("Not Found 404") log_to_file("Not Found 404: {}".format(value)) # Ignore this, used for flow control elif e_type == IgnoredException: return else: if isinstance(args[0], (User, Member)): readable = "{}:{} (guild:{})".format(args[0].name, args[0].id, args[0].guild.id) elif isinstance(args[0], Message): readable = "'{}' by {} (guild:{})".format(args[0].content, args[0].author.name, args[0].guild.id) elif isinstance(args[0], Guild): readable = "{} (guild)({})".format(args[0].name, args[0].id) else: try: readable = "__dict__ of {}: ".format(type(args[0]), args[0].__dict__) except AttributeError: readable = "__slots__ of {}: ".format(type(args[0]), args[0].__slots__) log_to_file("EXCEPTION in {}: {}".format(event, readable), "bug") exc = traceback.format_exc() traceback.print_exc() log_to_file(exc, "bug") log.warning("Something went wrong, see bugs.txt")
async def on_message(self, message, **kwargs): trans = self.trans prefix = kwargs.get("prefix") lang = kwargs.get("lang") # Check if this is a valid command if not is_valid_command(message.content, commands, prefix): return else: self.stats.add(MESSAGE) def startswith(*matches): for match in matches: if message.content.startswith(match): return True return False # !cat gif/jpg/png if startswith(prefix + "cat"): fmt = str(message.content[len(prefix + "cat"):]).strip(" ") # GIF is the default type! if fmt == "jpg": type_ = "jpg" elif fmt == "png": type_ = "png" else: type_ = "gif" pic = await self.cats.random_cat(type_) if pic: # Teal (blue-ish) embed = Embed(colour=Colour(0x00796b)) embed.set_image(url=pic) embed.set_footer(text=trans.get("MSG_CAT_FOOTER", lang)) await message.channel.send(embed=embed) else: await message.channel.send(trans.get("MSG_CAT_FAILED", lang)) self.stats.add(IMAGE_SENT) # !xkcd random/number/latest elif startswith(prefix + "xkcd"): fmt = str(message.content[len(prefix + "xkcd"):]).strip(" ") # Decides mode fetch = "random" if fmt: if is_number(fmt): # Check if number is valid if int(fmt) > self.xkcd.last_num: await message.channel.send( trans.get("MSG_XKCD_NO_SUCH", lang)) return else: fetch = "number" elif fmt == trans.get("INFO_RANDOM", lang) or fmt == "random": fetch = "random" # Any other argument means latest else: fetch = "latest" # Default: random else: fetch == "random" if fetch == "random": xkcd = await self.xkcd.get_random_xkcd() elif fetch == "number": xkcd = await self.xkcd.get_xkcd_by_number(fmt) # Can only mean latest else: xkcd = await self.xkcd.get_latest_xkcd() # In case something went wrong if not xkcd: await message.channel.send(trans.get("MSG_XKCD_FAILED", lang)) log_to_file("XKCD: string {}, fetch: {}, got None".format( fmt, fetch)) xkcd_link = self.xkcd.make_link(xkcd["num"]) embed = Embed(title=trans.get("MSG_XKCD", lang).format(xkcd["num"]), description=filter_text(xkcd["safe_title"])) embed.set_image(url=xkcd["img"]) embed.set_footer( text=trans.get("MSG_XKCD_SOURCE", lang).format(xkcd_link)) await message.channel.send(embed=embed) # !joke (yo mama/chuck norris) elif startswith(prefix + "joke"): content = filter_text(self.joke.random_joke()) embed = Embed(description=content) await message.channel.send(embed=embed)