def on_banphrase_update(self, data): try: banphrase_id = int(data["id"]) except (KeyError, ValueError): log.warning("No banphrase ID found in on_banphrase_update") return False updated_banphrase = find( lambda banphrase: banphrase.id == banphrase_id, self.banphrases) if updated_banphrase: with DBManager.create_session_scope( expire_on_commit=False) as db_session: db_session.add(updated_banphrase) db_session.refresh(updated_banphrase) db_session.expunge(updated_banphrase) else: with DBManager.create_session_scope( expire_on_commit=False) as db_session: updated_banphrase = (db_session.query(Banphrase).filter_by( id=banphrase_id).one_or_none()) db_session.expunge_all() if updated_banphrase is not None: self.db_session.add(updated_banphrase.data) if updated_banphrase: if updated_banphrase not in self.banphrases: self.banphrases.append(updated_banphrase) if (updated_banphrase.enabled is True and updated_banphrase not in self.enabled_banphrases): self.enabled_banphrases.append(updated_banphrase) for banphrase in self.enabled_banphrases: if banphrase.enabled is False: self.enabled_banphrases.remove(banphrase)
def __init__(self, config, args): self.config = config self.args = args self.private_loop = asyncio.get_event_loop() self.private_loop.set_exception_handler(custom_exception_handler) self.discord_token = self.config["main"]["discord_token"] ScheduleManager.init(self.private_loop) DBManager.init(self.config["main"]["db"]) ActionParser.bot = self # redis redis_options = {} if "redis" in config: redis_options = dict(config.items("redis")) RedisManager.init(**redis_options) utils.wait_for_redis_data_loaded(RedisManager.get()) # SQL migrations try: with DBManager.create_dbapi_connection_scope() as sql_conn: sql_migratable = DatabaseMigratable(sql_conn) sql_migration = Migration(sql_migratable, greenbot.migration_revisions.db, self) sql_migration.run() except ValueError as error: log.error(error) HandlerManager.init_handlers() self.movienight_api = MovieNightAPI(self, self.config["wsc"], self.config["wowza_cdn"]) HandlerManager.add_handler("parse_command_from_message", self.parse_command_from_message) self.bot_name = self.config["main"]["bot_name"] self.command_prefix = self.config["discord"]["command_prefix"] self.settings = { "discord_token": self.discord_token, "bot_name": self.bot_name, "command_prefix": self.command_prefix, "discord_guild_id": self.config["discord"]["discord_guild_id"], } HandlerManager.add_handler("discord_ready", self.wait_discord_load) self.discord_bot = DiscordBotManager( bot=self, settings=self.settings, redis=RedisManager.get(), private_loop=self.private_loop, ) self.twitter_manager = TwitterManager(self) self.filters = Filters(self, self.discord_bot) self.functions = Functions(self, self.filters)
def edit_command(self, command_to_edit, **options): if "group" in options: self.remove_command_aliases(command_to_edit) command_to_edit.set(**options) command_to_edit.data.set(**options) DBManager.session_add_expunge(command_to_edit) if "group" in options: self.add_db_command_aliases(command_to_edit) self.commit() self.rebuild()
async def giveaway_start(self, bot, author, channel, message, args): with DBManager.create_session_scope() as db_session: current_giveaway = Giveaway._get_current_giveaway(db_session) if current_giveaway: await self.bot.say( channel=channel, message= "There is already a giveaway running. Please use !wipegiveaway before you start a new one. (Don't forget to chose a winner before you end!)", ignore_escape=True) return False desc_array = re.findall(r'"([^"]*)"', message) if not len(desc_array) == 2: await self.bot.say( channel=channel, message= 'Please set 2 arguments between quotation marks. `!startgiveaway "<item>" "<deadline>"`\nExample: `!startgiveaway "a new GTX2900" "10 days"`', ignore_escape=True) return False if Giveaway._create(db_session, str(author.id), desc_array[0], desc_array[1]): await self.bot.say( channel=channel, message= "New giveaway was started! Use !giveawaywinner to chose a winner when the time has passed.", ignore_escape=True) return True await self.bot.say( channel=channel, message= "An unknown error has occurred, please contact a moderator", ignore_escape=True) return False
def post(self, row_id, **options): args = self.post_parser.parse_args() try: new_state = int(args["new_state"]) except (ValueError, KeyError): return {"error": "Invalid `new_state` parameter."}, 400 with DBManager.create_session_scope() as db_session: row = db_session.query(Module).filter_by(id=row_id).one_or_none() if not row: return {"error": "Module with this ID not found"}, 404 if validate_module(row_id) is False: return {"error": "cannot modify module"}, 400 row.enabled = True if new_state == 1 else False db_session.commit() payload = {"id": row.id, "new_state": row.enabled} AdminLogManager.post( "Module toggled", options["user"].discord_id, "Enabled" if row.enabled else "Disabled", row.id, ) SocketClientManager.send("module.update", payload) return {"success": "successful toggle", "new_state": new_state}
async def on_message(self, message): member = self.bot.guild.get_member(message.author.id) if isinstance(message.author, discord.Member) and ( message.guild != self.bot.guild ): return with DBManager.create_session_scope() as db_session: user = User._create_or_get_by_discord_id( db_session, message.author.id, user_name=str(member) if member else str(message.author), ) Message._create( db_session, message.id, message.author.id, message.channel.id if isinstance(message.author, discord.Member) else None, message.content, ) db_session.commit() HandlerManager.trigger( "discord_message", message_raw=message, message=message.content, author=message.author, user_level=user.level if user else 50, channel=message.channel if isinstance(message.author, discord.Member) else None, whisper=not isinstance(message.author, discord.Member), )
def func_ban_member(self, args, extra={}): if len(args) == 0: return "Invalid User", None member = self.get_member(args[0][3:][:-1]) author = extra["author"] if not member: return "Member not found", None with DBManager.create_session_scope() as db_session: author_user = User._create_or_get_by_discord_id( db_session, str(author.id), user_name=str(author)) member_user = User._create_or_get_by_discord_id( db_session, str(member.id), user_name=str(member)) if author.id == member.id: return "You cannot ban yourself :)", None if author_user.level == member_user.level: return "You cannot ban someone who has the same level as you :)", None elif author_user.level < member_user.level: return "You cannot ban someone who is a higher level than you :)", None timeout_in_seconds = int( args[1] if len(args) > 2 and args[1] != "" else 0) delete_message_days = int( args[2] if len(args) > 3 and args[2] != "" else 0) reason = args[3] if len(args) == 4 else "" message = f"Member {member.mention} has been banned!" self.ban( user=member, timeout_in_seconds=timeout_in_seconds, delete_message_days=delete_message_days, reason=f"{reason}\nBanned by {author}", ) return message, None
async def untimeout_user(self, bot, author, channel, message, args): command_args = message.split(" ") if message else [] if len(command_args) == 0: await self.bot.say( channel=channel, message=f"!untimeout (here) <User mention> (reason...)") return False member = self.bot.filters.get_member([command_args[0]], None, {})[0] if not member: await self.bot.say(channel=channel, message=f"Cant find member, {command_args[0]}") return False unban_reason = " ".join(command_args[1:]) with DBManager.create_session_scope() as db_session: success, resp = await self.bot.timeout_manager.untimeout_user( db_session, member, author, unban_reason) if success: await self.bot.say( channel=channel, message=f"Member {member.mention} has been untimedout") return True self.bot.say(channel=channel, message=resp) return False
def wait_discord_load(self): self.socket_manager = SocketManager(self.bot_name, self.execute_now) self.module_manager = ModuleManager(self.socket_manager, bot=self).load() self.commands = CommandManager( socket_manager=self.socket_manager, module_manager=self.module_manager, bot=self, ).load() HandlerManager.trigger("manager_loaded") # promote the admin to level 2000 owner = self.config["main"].get("owner_id", None) if owner is None: log.warning( "No admin user specified. See the [main] section in the example config for its usage." ) else: with DBManager.create_session_scope() as db_session: owner = User._create_or_get_by_discord_id( db_session, str(owner)) if owner is None: log.warning( "The login name you entered for the admin user does not exist on twitch. " "No admin user has been created.") else: owner.level = 2000
async def run_action(self, bot, author, channel, message, args): cur_time = greenbot.utils.now().timestamp() with DBManager.create_session_scope() as db_session: user = User._create_or_get_by_discord_id(db_session, str(author.id), str(author)) with user.spend_currency_context( self.cost if args["user_level"] < Command.BYPASS_DELAY_LEVEL else 0): ret = await self.action.run(bot, author, channel, message, args) if not ret: raise FailedCommand("return currency") # Only spend points, and increment num_uses if the action succeded if self.data is not None: self.data.num_uses += 1 self.data.last_date_used = greenbot.utils.now() # TODO: Will this be an issue? self.last_run = cur_time self.last_run_by_user[str(author.id)] = cur_time if ret == "return currency": db_session.commit() raise FailedCommand("return currency")
def load(self, do_reload=True): """ Load module classes """ from greenbot.modules import available_modules self.all_modules = [module(self.bot) for module in available_modules] with DBManager.create_session_scope() as db_session: # Make sure there's a row in the DB for each module that's available db_modules = db_session.query(Module).all() for module in self.all_modules: mod = find( lambda db_module, registered_module=module: db_module.id == registered_module.ID, db_modules, ) if mod is None: log.info(f"Creating row in DB for module {module.ID}") mod = Module(module.ID, enabled=module.ENABLED_DEFAULT) db_session.add(mod) if do_reload is True: self.reload() return self
async def initial_unbans(self): try: data = json.loads(self.redis.get(f"{self.bot.bot_name}:timeouts-discord")) for user in data: unban_date = utils.parse_date(data[user]["unban_date"]) time_now = utils.now() resp_timeout = data.get("resp_timeout", "") resp_timeout += " after " if resp_timeout else "" if unban_date < time_now: ScheduleManager.execute_now( method=self.unban, args=[ data[user]["discord_id"], f"Unbanned by timer{resp_timeout}", ], ) continue ScheduleManager.execute_delayed( delay=(unban_date - time_now).seconds, method=self.unban, args=[data[user]["discord_id"], f"Unbanned by timer{resp_timeout}"], ) except Exception as e: log.exception(e) self.redis.set(f"{self.bot.bot_name}:timeouts-discord", json.dumps({})) with DBManager.create_session_scope() as db_session: unnamed_users = db_session.query(User).filter_by(user_name="").all() for user in unnamed_users: member = self.get_member(int(user.discord_id)) if not member: db_session.delete(user) continue user.user_name = str(member)
async def func_level(self, args, extra={}): if len(args) != 2: return "Invalid Comand Args", None member = self.filters.get_member([args[0]], None, {})[0] if not member: return f"Invalid Member {args[0]}", None if member == extra["author"]: return "You cannot edit your own level :)", None level = args[1] try: level = int(level) except ValueError: return f"Invalid level (1-2000) {args[1]}", None if level >= extra["user_level"]: return "You cannot set a level higher then your own!", None with DBManager.create_session_scope() as db_session: user = User._create_or_get_by_discord_id(db_session, str(member.id), str(member)) if user.level >= extra["user_level"]: return ( "You cannot set a level of a user with a higher then your own!", None, ) user.level = level return f"Level, {level}, set for {member.mention}!", None
async def func_kick_member(self, args, extra={} ): # !kick <member_mention> <reason....> if len(args) != 2: return "Invalid Comand Args", None author = extra["author"] member = self.filters.get_member([args[0]], None, {})[0] if not member: return "Member not found", None with DBManager.create_session_scope() as db_session: if extra["user_level"] < self.bot.psudo_level_member( db_session, member): return ( "You cannot kick someone who has the same or a higher level than you :)", None, ) reason = args[1] resp = await self.bot.kick(member, f"{reason}\nKicked by {author}") if not resp: return f"Failed to kick member {member}!", None return f"Member {member.mention} has been kicked by {author.mention}!", None
def process_messages(self): with DBManager.create_session_scope() as db_session: regular_role = self.bot.get_role(self.settings["regular_role_id"]) sub_role = self.bot.get_role(self.settings["sub_role_id"]) for member in regular_role.members: count = Message._get_week_count_user(db_session, str(member.id)) if (count < self.settings["min_msgs_per_week"] or sub_role not in member.roles): self.bot.remove_role(member, regular_role) db_session.commit() messages = Message._get_last_hour(db_session) channels_to_listen_in = self.settings[ "channels_to_listen_in"].split(" ") if len( self.settings["channels_to_listen_in"]) != 0 else [] for message in messages: if message.channel_id not in channels_to_listen_in and len( channels_to_listen_in) != 0: continue count = Message._get_day_count_user(db_session, message.user_id) if message.user_id != str(self.bot.bot_id): if count < self.settings["daily_max_msgs"] - 1: message.user.points += self.settings["hourly_credit"] elif count == self.settings["daily_max_msgs"] - 1: message.user.points += self.settings["daily_limit"] message.credited = True db_session.commit() for user in User._get_users_with_points( db_session, self.settings["min_regular_points"]): member = self.bot.get_member(user.discord_id) if (not member or sub_role not in member.roles or regular_role in member.roles): continue self.bot.add_role(member, regular_role)
def is_enabled(cls): with DBManager.create_session_scope() as db_session: db_module = db_session.query(Module).filter_by( id=cls.ID).one_or_none() if db_module is None: return cls.ENABLED_DEFAULT else: return db_module.enabled
def commands_edit(command_id, **options): with DBManager.create_session_scope() as db_session: command = (db_session.query(Command).options( joinedload(Command.data).joinedload( CommandData.user)).filter_by(id=command_id).one_or_none()) with DBManager.create_session_scope() as db_session: user = (db_session.query(User).filter_by( discord_id=session["user"]["discord_id"]).one_or_none()) if command.action.functions and user.level < 1500: abort(403) if command is None: return render_template("admin/command_404.html"), 404 return render_template( "admin/edit_command.html", command=command, user=options.get("user", None), )
def get_user(self, key, extra={}): user = (self.get_member(extra["argument"][3:][:-1]) if extra["argument"] else None) if not user: user = extra["author"] with DBManager.create_session_scope() as db_session: db_user = User._create_or_get_by_discord_id(db_session, user.id) return getattr(db_user, key) if db_user else None
def banphrases(**options): with DBManager.create_session_scope() as db_session: banphrases = (db_session.query(Banphrase).options( joinedload(Banphrase.data).joinedload(BanphraseData.user), joinedload(Banphrase.data).joinedload(BanphraseData.user2), ).order_by(Banphrase.id).all()) return render_template("admin/banphrases.html", banphrases=banphrases)
def timers_edit(timer_id, **options): with DBManager.create_session_scope() as db_session: timer = db_session.query(Timer).filter_by(id=timer_id).one_or_none() if timer is None: return render_template("admin/timer_404.html"), 404 return render_template("admin/create_timer.html", timer=timer)
def get(id): with DBManager.create_session_scope() as db_session: user = User.find_by_id(db_session, id) if user is None: return {"error": "Not found"}, 404 # the aliases like above are not needed here since this API endpoint is new since version 1.38 return user.jsonify()
def timers(**options): with DBManager.create_session_scope() as db_session: return render_template( "admin/timers.html", timers=db_session.query(Timer).all(), created=session.pop("timer_created_id", None), edited=session.pop("timer_edited_id", None), )
def on_timer_update(self, data): try: timer_id = int(data["id"]) except (KeyError, ValueError): log.warning("No timer ID found in on_timer_update") return False updated_timer = find(lambda timer: timer.id == timer_id, self.timers) if updated_timer: with DBManager.create_session_scope( expire_on_commit=False) as db_session: db_session.add(updated_timer) db_session.refresh(updated_timer) updated_timer.refresh_action() db_session.expunge(updated_timer) else: with DBManager.create_session_scope( expire_on_commit=False) as db_session: updated_timer = (db_session.query(Timer).filter_by( id=timer_id).one_or_none()) # Add the updated timer to the timer lists if required if updated_timer: if updated_timer not in self.timers: self.timers.append(updated_timer) if updated_timer not in self.online_timers: if updated_timer.interval_online > 0: self.online_timers.append(updated_timer) updated_timer.refresh_tts() if updated_timer not in self.offline_timers: if updated_timer.interval_offline > 0: self.offline_timers.append(updated_timer) updated_timer.refresh_tts() for timer in self.online_timers: if timer.enabled is False or timer.interval_online <= 0: self.online_timers.remove(timer) for timer in self.offline_timers: if timer.enabled is False or timer.interval_offline <= 0: self.offline_timers.remove(timer) return True
def remove_command(self, command): self.remove_command_aliases(command) with DBManager.create_session_scope() as db_session: self.db_session.expunge(command.data) db_session.delete(command.data) db_session.delete(command) self.rebuild()
def discord_auth(): discord.callback() user = discord.fetch_user() with DBManager.create_session_scope(expire_on_commit=False) as db_session: session["user"] = User._create_or_get_by_discord_id( db_session, str(user.id), str(user) ).jsonify() session["user_displayname"] = str(user) next_url = session.get("state", "/") return redirect(next_url)
def banphrases_edit(banphrase_id, **options): with DBManager.create_session_scope() as db_session: banphrase = (db_session.query(Banphrase).filter_by( id=banphrase_id).one_or_none()) if banphrase is None: return render_template("admin/banphrase_404.html"), 404 return render_template("admin/create_banphrase.html", banphrase=banphrase)
def add_entry(entry_type, source, message, data={}): with DBManager.create_session_scope() as db_session: entry_object = AdminLogEntry( type=entry_type, user_id=source, message=message, created_at=utils.now(), data=data, ) db_session.add(entry_object)
def get(self, timer_id, **options): with DBManager.create_session_scope() as db_session: timer = db_session.query(Timer).filter_by( id=timer_id).one_or_none() if timer is None: return {"error": "Invalid timer ID"}, 404 AdminLogManager.post("Timer removed", options["user"].discord_id, timer.name) db_session.delete(timer) SocketClientManager.send("timer.remove", {"id": timer.id}) return {"success": "good job"}
def __init__(self, bot): self.bot = bot self.banphrases = [] self.enabled_banphrases = [] self.db_session = DBManager.create_session(expire_on_commit=False) if self.bot: self.bot.socket_manager.add_handler("banphrase.update", self.on_banphrase_update) self.bot.socket_manager.add_handler("banphrase.remove", self.on_banphrase_remove)
def db_settings(cls): settings = {} with DBManager.create_session_scope() as session: module = session.query(Module).filter(Module.id == cls.ID).one() if module.settings is not None: try: settings = json.loads(module.settings) except ValueError: pass return settings