Esempio n. 1
0
async def loggingOnTwitchalertEdit(cls: "PhaazebotDiscord",
                                   Settings: DiscordServerSettings,
                                   **kwargs) -> None:
    """
	Logs the event when someone changes a twitch alert (mostly) via web.
	If track option `Twitchalert.edit` is active, it will send a message to discord

	Required keywords:
	------------------
	* `ChangeMember` - discord.Member
	* `twitch_channel` - str
	* `discord_channel_id` - str
	* `changes` - dict
	"""
    logging_signature: str = "Twitchalert.edit"
    ChangeMember: discord.Member = kwargs["ChangeMember"]
    twitch_channel: str = kwargs["twitch_channel"]
    discord_channel_id: str = kwargs["discord_channel_id"]
    changes: str = kwargs["changes"]

    Chan: discord.TextChannel = getDiscordChannelFromString(
        cls, ChangeMember.guild, discord_channel_id, required_type="text")
    discord_channel_name: str = Chan.name if Chan else "(Unknown)"

    new_log_id: int = cls.BASE.PhaazeDB.insertQuery(
        table="discord_log",
        content={
            "guild_id":
            Settings.server_id,
            "event_value":
            TRACK_OPTIONS[logging_signature],
            "initiator_id":
            str(ChangeMember.id),
            "content":
            f"{ChangeMember.name} changed the Twitch-Alert for {twitch_channel} in #{discord_channel_name}: {str(changes)}"
        })

    if not (TRACK_OPTIONS[logging_signature] & Settings.track_value):
        return  # track option not active, skip message to discord server

    TargetChannel: discord.TextChannel = getDiscordChannelFromString(
        cls, ChangeMember.guild, Settings.track_channel, required_type="text")
    if not TargetChannel: return  # no channel found

    Emb: discord.Embed = discord.Embed(
        title=f"Log Event - [{logging_signature}]",
        description=f"{ChangeMember.name} changed a Twitch-Alert.",
        timestamp=datetime.datetime.now(),
        color=EVENT_COLOR_WARNING,
        url=makeWebAccessLink(cls, ChangeMember.guild.id, new_log_id))
    Emb.set_thumbnail(
        url=ChangeMember.avatar_url or ChangeMember.default_avatar_url)
    Emb.add_field(name="Twitch channel:", value=twitch_channel, inline=False)

    try:
        await TargetChannel.send(embed=Emb)
    except Exception as E:
        cls.BASE.Logger.warning(
            f"Can't log message: {E} {traceback.format_exc()}")
Esempio n. 2
0
async def loggingOnAssignroleCreate(cls: "PhaazebotDiscord",
                                    Settings: DiscordServerSettings,
                                    **kwargs) -> None:
    """
	Logs the event when someone creates a new assignrole via web.
	If track option `Assignrole.create` is active, it will send a message to discord

	Required keywords:
	------------------
	* `Creator` - discord.Member
	* `assign_role_id` - str
	* `trigger` - str
	"""
    logging_signature: str = "Assignrole.create"
    Creator: discord.Member = kwargs["Creator"]
    assign_role_id: str = kwargs["assign_role_id"]
    trigger: str = kwargs["trigger"]

    AssignRole: discord.Role = getDiscordRoleFromString(
        cls, Creator.guild, assign_role_id)
    assign_role_name: str = AssignRole.name if AssignRole else "(Unknown)"

    new_log_id: int = cls.BASE.PhaazeDB.insertQuery(
        table="discord_log",
        content={
            "guild_id":
            Settings.server_id,
            "event_value":
            TRACK_OPTIONS[logging_signature],
            "initiator_id":
            str(Creator.id),
            "content":
            f"{Creator.name} created a new assign role {assign_role_name} with {trigger=}"
        })

    if not (TRACK_OPTIONS[logging_signature] & Settings.track_value):
        return  # track option not active, skip message to discord server

    TargetChannel: discord.TextChannel = getDiscordChannelFromString(
        cls, Creator.guild, Settings.track_channel, required_type="text")
    if not TargetChannel: return  # no channel found

    Emb: discord.Embed = discord.Embed(
        title=f"Log Event - [{logging_signature}]",
        description=f"{Creator.name} created a assignrole",
        timestamp=datetime.datetime.now(),
        color=EVENT_COLOR_POSITIVE,
        url=makeWebAccessLink(cls, Creator.guild.id, new_log_id))
    Emb.set_thumbnail(url=Creator.avatar_url or Creator.default_avatar_url)
    Emb.add_field(name="Trigger:", value=trigger, inline=False)
    Emb.add_field(name="Linked with role:",
                  value=assign_role_name,
                  inline=False)

    try:
        await TargetChannel.send(embed=Emb)
    except Exception as E:
        cls.BASE.Logger.warning(
            f"Can't log message: {E} {traceback.format_exc()}")
Esempio n. 3
0
async def loggingOnLevelmedalDelete(cls: "PhaazebotDiscord",
                                    Settings: DiscordServerSettings,
                                    **kwargs) -> None:
    """
	Logs the event when someone deletes a discordmember medal via web.
	If track option `Levelmedal.delete` is active, it will send a message to discord

	Required keywords:
	------------------
	* `Deleter` - discord.Member
	* `medal_member_id` - str
	* `medal_name` - str
	"""
    logging_signature: str = "Levelmedal.delete"
    Deleter: discord.Member = kwargs["Deleter"]
    medal_member_id: str = kwargs["medal_member_id"]
    medal_name: str = kwargs["medal_name"]

    MedalMember: discord.Member = getDiscordMemberFromString(
        cls, Deleter.guild, medal_member_id)
    medal_member_name: str = MedalMember.name if MedalMember else "(Unknown)"

    new_log_id: int = cls.BASE.PhaazeDB.insertQuery(
        table="discord_log",
        content={
            "guild_id":
            Settings.server_id,
            "event_value":
            TRACK_OPTIONS[logging_signature],
            "initiator_id":
            str(Deleter.id),
            "content":
            f"{Deleter.name} removed a medal from: {medal_member_name}, old medal: {medal_name}"
        })

    if not (TRACK_OPTIONS[logging_signature] & Settings.track_value):
        return  # track option not active, skip message to discord server

    TargetChannel: discord.TextChannel = getDiscordChannelFromString(
        cls, Deleter.guild, Settings.track_channel, required_type="text")
    if not TargetChannel: return  # no channel found

    Emb: discord.Embed = discord.Embed(
        title=f"Log Event - [{logging_signature}]",
        description=f"{Deleter.name} removed a medal",
        timestamp=datetime.datetime.now(),
        color=EVENT_COLOR_NEGATIVE,
        url=makeWebAccessLink(cls, Deleter.guild.id, new_log_id))
    Emb.set_thumbnail(url=Deleter.avatar_url or Deleter.default_avatar_url)
    Emb.add_field(name="Target member:", value=medal_member_name, inline=False)
    Emb.add_field(name="Medal:", value=medal_name, inline=False)

    try:
        await TargetChannel.send(embed=Emb)
    except Exception as E:
        cls.BASE.Logger.warning(
            f"Can't log message: {E} {traceback.format_exc()}")
Esempio n. 4
0
async def loggingOnQuoteEdit(cls: "PhaazebotDiscord",
                             Settings: DiscordServerSettings,
                             **kwargs) -> None:
    """
	Logs the event when someone edits a quote, doesn't matter if in discord or web. (You can only do this in web duh)
	If track option `Quote.create` is active, it will send a message to discord

	Required keywords:
	------------------
	* `ChangeMember` - discord.Member
	* `quote_id` - str
	* `old_content` - str
	* `new_content` - str
	"""
    logging_signature: str = "Quote.edit"
    ChangeMember: discord.Member = kwargs["ChangeMember"]
    quote_id: str = kwargs["quote_id"]
    old_content: str = kwargs["old_content"]
    new_content: str = kwargs["new_content"]

    new_log_id: int = cls.BASE.PhaazeDB.insertQuery(
        table="discord_log",
        content={
            "guild_id":
            Settings.server_id,
            "event_value":
            TRACK_OPTIONS[logging_signature],
            "initiator_id":
            str(ChangeMember.id),
            "content":
            f"{ChangeMember.name} changed quote #{quote_id}: {old_content} -> {new_content}"
        })

    if not (TRACK_OPTIONS[logging_signature] & Settings.track_value):
        return  # track option not active, skip message to discord server

    TargetChannel: discord.TextChannel = getDiscordChannelFromString(
        cls, ChangeMember.guild, Settings.track_channel, required_type="text")
    if not TargetChannel: return  # no channel found

    Emb: discord.Embed = discord.Embed(
        title=f"Log Event - [{logging_signature}]",
        description=f"{ChangeMember.name} changed a quote.",
        timestamp=datetime.datetime.now(),
        color=EVENT_COLOR_WARNING,
        url=makeWebAccessLink(cls, ChangeMember.guild.id, new_log_id))
    Emb.set_thumbnail(
        url=ChangeMember.avatar_url or ChangeMember.default_avatar_url)
    Emb.add_field(name="Content:",
                  value=f"{old_content[:500]} -> {new_content[:500]}",
                  inline=True)

    try:
        await TargetChannel.send(embed=Emb)
    except Exception as E:
        cls.BASE.Logger.warning(
            f"Can't log message: {E} {traceback.format_exc()}")
Esempio n. 5
0
async def loggingOnAssignroleEdit(cls: "PhaazebotDiscord",
                                  Settings: DiscordServerSettings,
                                  **kwargs) -> None:
    """
	Logs the event when someone edits assignrole via web.
	If track option `Assignrole.edit` is active, it will send a message to discord

	Required keywords:
	------------------
	* `Editor` - discord.Member
	* `assign_role_trigger` - str
	* `changes` - dict
	"""
    logging_signature: str = "Assignrole.edit"
    Editor: discord.Member = kwargs["Editor"]
    assign_role_trigger: str = kwargs["assign_role_trigger"]
    changes: dict = kwargs["changes"]

    new_log_id: int = cls.BASE.PhaazeDB.insertQuery(
        table="discord_log",
        content={
            "guild_id":
            Settings.server_id,
            "event_value":
            TRACK_OPTIONS[logging_signature],
            "initiator_id":
            str(Editor.id),
            "content":
            f"{Editor.name} edited the assign role with trigger='{assign_role_trigger}' changes: {str(changes)}"
        })

    if not (TRACK_OPTIONS[logging_signature] & Settings.track_value):
        return  # track option not active, skip message to discord server

    TargetChannel: discord.TextChannel = getDiscordChannelFromString(
        cls, Editor.guild, Settings.track_channel, required_type="text")
    if not TargetChannel: return  # no channel found

    Emb: discord.Embed = discord.Embed(
        title=f"Log Event - [{logging_signature}]",
        description=f"{Editor.name} edited a assignrole",
        timestamp=datetime.datetime.now(),
        color=EVENT_COLOR_WARNING,
        url=makeWebAccessLink(cls, Editor.guild.id, new_log_id))
    Emb.set_thumbnail(url=Editor.avatar_url or Editor.default_avatar_url)
    Emb.add_field(name="Role trigger:",
                  value=assign_role_trigger,
                  inline=False)

    try:
        await TargetChannel.send(embed=Emb)
    except Exception as E:
        cls.BASE.Logger.warning(
            f"Can't log message: {E} {traceback.format_exc()}")
Esempio n. 6
0
async def loggingOnMemberJoin(cls: "PhaazebotDiscord",
                              Settings: DiscordServerSettings,
                              **kwargs) -> None:
    """
	Logs the event when a member is added (joins) a guild.
	If track option `Member.join` is active, it will send a message to discord

	Required keywords:
	------------------
	* `NewMember` - discord.Member

	Optional keywords:
	------------------
	* `link_in_name` - bool : (Default: False)
	"""
    logging_signature: str = "Member.join"
    NewMember: discord.Member = kwargs["NewMember"]
    link_in_name: bool = bool(kwargs.get("link_in_name", False))

    new_log_id: int = cls.BASE.PhaazeDB.insertQuery(
        table="discord_log",
        content={
            "guild_id": Settings.server_id,
            "event_value": TRACK_OPTIONS[logging_signature],
            "initiator_id": str(NewMember.id),
            "content": f"{NewMember.name} joined the server"
        })

    if not (TRACK_OPTIONS[logging_signature] & Settings.track_value):
        return  # track option not active, skip message to discord server

    TargetChannel: discord.TextChannel = getDiscordChannelFromString(
        cls, NewMember.guild, Settings.track_channel, required_type="text")
    if not TargetChannel: return  # no channel found

    Emb: discord.Embed = discord.Embed(
        title=f"Log Event - [{logging_signature}]",
        description=
        f"Initial name: {NewMember.name}\nMention: {NewMember.mention}\nID: {NewMember.id}",
        timestamp=datetime.datetime.now(),
        color=EVENT_COLOR_POSITIVE,
        url=makeWebAccessLink(cls, NewMember.guild.id, new_log_id))
    Emb.set_thumbnail(url=NewMember.avatar_url or NewMember.default_avatar_url)
    if link_in_name:
        Emb.add_field(name=":warning: Blocked public announcements",
                      value="Link in name",
                      inline=True)

    try:
        await TargetChannel.send(embed=Emb)
    except Exception as E:
        cls.BASE.Logger.warning(
            f"Can't log message: {E} {traceback.format_exc()}")
Esempio n. 7
0
async def eventOnMemberRemove(cls: "PhaazebotDiscord",
                              Member: discord.Member) -> None:
    """
	Get's triggered everytime a member leaves a guild
	the following action may be taken (in this order):
	* Send logging message
	* Send a leave message to guild channel
	* set member inactive in levels table
	"""

    Settings: DiscordServerSettings = await getDiscordSeverSettings(
        cls, Member.guild.id)
    link_in_name: bool = bool(ContainsLink.match(Member.name))

    # logging message
    log_coro: Coroutine = loggingOnMemberRemove(cls,
                                                Settings,
                                                OldMember=Member,
                                                link_in_name=link_in_name)
    asyncio.ensure_future(log_coro, loop=cls.BASE.DiscordLoop)

    # send leave message
    if Settings.leave_chan and Settings.leave_msg and (not link_in_name):

        LeaveChan: discord.TextChannel = getDiscordChannelFromString(
            cls, Member.guild, Settings.leave_chan, required_type="text")
        if LeaveChan:
            welcome_msg_vars: dict = {
                "user-name": Member.name,
                "user-mention": Member.mention,
                "server-name": Member.guild.name,
                "member-count": str(Member.guild.member_count)
            }
            finished_message: str = await responseFormatter(
                cls,
                Settings.leave_msg,
                var_dict=welcome_msg_vars,
                enable_special=True,
                DiscordGuild=Member.guild)
            try:
                finished_message = finished_message[:1997]
                await LeaveChan.send(finished_message)
            except Exception as E:
                cls.BASE.Logger.warning(
                    f"Can't send leave message: {E} {traceback.format_exc()}")

    # set member inactive
    cls.BASE.PhaazeDB.updateQuery(table="discord_user",
                                  content={"on_server": 0},
                                  where="guild_id = %s AND member_id = %s",
                                  where_values=(str(Member.guild.id),
                                                str(Member.id)))
Esempio n. 8
0
async def loggingOnCommandCreate(cls: "PhaazebotDiscord",
                                 Settings: DiscordServerSettings,
                                 **kwargs) -> None:
    """
	Logs the event when someone creates a new command (mostly) via web.
	If track option `Command.create` is active, it will send a message to discord

	Required keywords:
	------------------
	* `Creator` - discord.Member
	* `command_trigger` - str
	* `command_info` - dict
	"""
    logging_signature: str = "Command.create"
    Creator: discord.Member = kwargs["Creator"]
    command_trigger: str = kwargs["command_trigger"]
    command_info: dict = kwargs["command_info"]

    new_log_id: int = cls.BASE.PhaazeDB.insertQuery(
        table="discord_log",
        content={
            "guild_id":
            Settings.server_id,
            "event_value":
            TRACK_OPTIONS[logging_signature],
            "initiator_id":
            str(Creator.id),
            "content":
            f"{Creator.name} created a new command ({command_trigger}) : {str(command_info)}"
        })

    if not (TRACK_OPTIONS[logging_signature] & Settings.track_value):
        return  # track option not active, skip message to discord server

    TargetChannel: discord.TextChannel = getDiscordChannelFromString(
        cls, Creator.guild, Settings.track_channel, required_type="text")
    if not TargetChannel: return  # no channel found

    Emb: discord.Embed = discord.Embed(
        title=f"Log Event - [{logging_signature}]",
        description=f"{Creator.name} created a command.",
        timestamp=datetime.datetime.now(),
        color=EVENT_COLOR_POSITIVE,
        url=makeWebAccessLink(cls, Creator.guild.id, new_log_id))
    Emb.set_thumbnail(url=Creator.avatar_url or Creator.default_avatar_url)
    Emb.add_field(name="New Trigger:", value=command_trigger, inline=True)

    try:
        await TargetChannel.send(embed=Emb)
    except Exception as E:
        cls.BASE.Logger.warning(
            f"Can't log message: {E} {traceback.format_exc()}")
Esempio n. 9
0
async def searchChannel(cls: "PhaazebotWeb", WebRequest: ExtendedRequest,
                        Data: WebRequestContent) -> Response:
    search_term: str = Data.getStr("term", "")
    guild_id: str = Data.getStr("guild_id", "", must_be_digit=True)

    if not guild_id:
        return await apiMissingData(cls,
                                    WebRequest,
                                    msg="invalid or missing 'guild_id'")

    Guild: discord.Guild = discord.utils.get(cls.BASE.Discord.guilds,
                                             id=int(guild_id))
    if not Guild:
        return await apiDiscordGuildUnknown(cls, WebRequest)

    Channel: Union[discord.TextChannel, discord.VoiceChannel,
                   discord.CategoryChannel] = getDiscordChannelFromString(
                       cls.BASE.Discord, Guild, search_term, contains=True)
    if not Channel: return await apiDiscordChannelNotFound(cls, WebRequest)

    data: dict = {
        "name": str(Channel.name),
        "id": str(Channel.id),
        "position": Channel.position,
    }

    if type(Channel) is discord.TextChannel:
        data["channel_type"] = "text"

    elif type(Channel) is discord.VoiceChannel:
        data["channel_type"] = "voice"

    elif type(Channel) is discord.CategoryChannel:
        data["channel_type"] = "category"

    else:
        data["channel_type"] = "unknown"

    return cls.response(text=json.dumps(dict(result=data, status=200)),
                        content_type="application/json",
                        status=200)
Esempio n. 10
0
async def apiDiscordConfigsGameEnabledChannelsCreate(
        cls: "PhaazebotWeb", WebRequest: ExtendedRequest) -> Response:
    """
	Default url: /api/discord/configs/gameenabledchannels/create
	"""
    Data: WebRequestContent = WebRequestContent(WebRequest)
    await Data.load()

    # get required stuff
    Create: StorageTransformer = StorageTransformer()
    Create["guild_id"] = Data.getStr("guild_id", UNDEFINED, must_be_digit=True)
    Create["channel_id"] = Data.getStr("channel_id",
                                       UNDEFINED,
                                       must_be_digit=True)

    # checks
    if not Create["guild_id"]:
        return await cls.Tree.Api.errors.apiMissingData(
            cls, WebRequest, msg="missing or invalid 'guild_id'")

    if not Create["channel_id"]:
        return await cls.Tree.Api.errors.apiMissingData(
            cls, WebRequest, msg="missing or invalid 'channel_id'")

    PhaazeDiscord: "PhaazebotDiscord" = cls.BASE.Discord
    Guild: discord.Guild = discord.utils.get(PhaazeDiscord.guilds,
                                             id=int(Create["guild_id"]))
    if not Guild:
        return await cls.Tree.Api.Discord.errors.apiDiscordGuildUnknown(
            cls, WebRequest)

    # get user info
    AuthDiscord: AuthDiscordWebUser = await authDiscordWebUser(cls, WebRequest)
    if not AuthDiscord.found:
        return await cls.Tree.Api.errors.apiMissingAuthorisation(
            cls, WebRequest)

    # get member
    CheckMember: discord.Member = Guild.get_member(
        int(AuthDiscord.User.user_id))
    if not CheckMember:
        return await cls.Tree.Api.Discord.errors.apiDiscordMemberNotFound(
            cls,
            WebRequest,
            guild_id=Create["guild_id"],
            user_id=AuthDiscord.User.user_id)

    # check permissions
    if not (CheckMember.guild_permissions.administrator
            or CheckMember.guild_permissions.manage_guild):
        return await cls.Tree.Api.Discord.errors.apiDiscordMissingPermission(
            cls,
            WebRequest,
            guild_id=Create["guild_id"],
            user_id=AuthDiscord.User.user_id)

    ActionChannel: discord.TextChannel = getDiscordChannelFromString(
        PhaazeDiscord, Guild, Create["channel_id"], required_type="text")
    if not ActionChannel:
        return await cls.Tree.Api.Discord.errors.apiDiscordChannelNotFound(
            cls, WebRequest, channel_id=Create["channel_id"])

    # check if already exists
    res: list = cls.BASE.PhaazeDB.selectQuery(
        """
		SELECT COUNT(*) AS `match`
		FROM `discord_enabled_gamechannel`
		WHERE `discord_enabled_gamechannel`.`guild_id` = %s
			AND `discord_enabled_gamechannel`.`channel_id` = %s""",
        (Create["guild_id"], Create["channel_id"]))

    if res[0]["match"]:
        return await cls.Tree.Api.Discord.Configs.Gameenabledchannels.errors.apiDiscordConfigsGameEnabledChannelExists(
            cls,
            WebRequest,
            channel_id=Create["channel_id"],
            channel_name=ActionChannel.name)

    cls.BASE.PhaazeDB.insertQuery(table="discord_enabled_gamechannel",
                                  content={
                                      "guild_id": Create["guild_id"],
                                      "channel_id": Create["channel_id"]
                                  })

    cls.BASE.Logger.debug(
        f"(API/Discord) Game enabled channel: {Create['guild_id']=} added: {Create['channel_id']=}",
        require="discord:configs")
    return cls.response(text=json.dumps(
        dict(msg="Game enabled channel: Added new entry",
             entry=Create["channel_id"],
             status=200)),
                        content_type="application/json",
                        status=200)
Esempio n. 11
0
async def responseFormatter(cls: "PhaazebotDiscord", content: str, *_x,
                            **kwargs) -> str:
    """
	This new formatter is support to ensure all formatting with all known regex
	means all [key] fields, if there are provided,
	but also special regex like <#name#>

	Info source keywords:
	---------------------
	* DiscordGuild `discord.Guild` : (Default: None) [ Enables (A) ]
	* CommandContext `DiscordCommandContext` : (Default: None) [ Enables (B) ]

	Optional keywords:
	------------------
	* enable_special `bool` (A) : (Default: False) [Replaces <#name#> or <#!id!#>]
	* enable_positions `bool` (B) : (Default: False) [Replaces $1, $6, etc.]
	* var_dict `dict` : (Default: None) [Replaces all keys with dict value]
	* VarRegex `re.Pattern` : (Default: CommandVariableString)
	"""

    DiscordGuild: discord.Guild = kwargs.get("DiscordGuild", None)
    CommandContext: DiscordCommandContext = kwargs.get("CommandContext", None)

    enable_special: bool = bool(kwargs.get("enable_special", False))
    enable_positions: bool = bool(kwargs.get("enable_positions", False))
    var_dict: dict = kwargs.get("var_dict", {})
    VarRegex: "re.Pattern" = kwargs.get("VarRegex",
                                        ReDiscord.CommandVariableString)

    # replaces [key1] [key2] with values from a same name dict
    if var_dict:
        VarHits: Iterator = re.finditer(VarRegex, content)
        for VarMatch in VarHits:
            key: str = VarMatch.group("name")

            if key in var_dict:
                content = content.replace(VarMatch.group(0), var_dict[key])

    # replaces $1 $5 $7 etc... at Positions
    if enable_positions and CommandContext:
        PositionMatch: Iterator = re.finditer(ReDiscord.CommandPosString,
                                              content)
        for PosMatch in PositionMatch:
            replacement: str = CommandContext.part(int(PosMatch.group("pos")))
            if not replacement: replacement = ""
            content = content.replace(PosMatch.group(0), replacement)

    # replaces <#name#> and <#!id!#> with the correct channel mention
    if enable_special and DiscordGuild:
        ChannelNameIter: Iterator = re.finditer(
            ReDiscord.SpecialStringChannelName, content)
        ChannelIDIter: Iterator = re.finditer(ReDiscord.SpecialStringChannelID,
                                              content)

        found: list = []

        for NameMatch in ChannelNameIter:
            found.append(NameMatch)
        for IDMatch in ChannelIDIter:
            found.append(IDMatch)

        if found:
            for Hit in found:
                ChannelToMention: discord.abc.GuildChannel = getDiscordChannelFromString(
                    cls, DiscordGuild, Hit.group(1))
                if ChannelToMention:
                    content = content.replace(Hit.group(0),
                                              ChannelToMention.mention)
                else:
                    content = content.replace(Hit.group(0), "(unknown)")

    return content
Esempio n. 12
0
async def eventOnMemberJoin(cls: "PhaazebotDiscord",
                            Member: discord.Member) -> None:
    """
	Get's triggered everytime a new member joins a guild,
	the following action may be taken (in this order):

	* Send logging message
	* Send a welcome message to guild channel
	* Send a private welcome message to the new member
	* Give the new member a predefined role
	* (if the member was on this guild before) set member active in levels table
	"""

    Settings: DiscordServerSettings = await getDiscordSeverSettings(
        cls, Member.guild.id)
    link_in_name: bool = bool(ContainsLink.match(Member.name))

    # logging message
    log_coro: Coroutine = loggingOnMemberJoin(cls,
                                              Settings,
                                              NewMember=Member,
                                              link_in_name=link_in_name)
    asyncio.ensure_future(log_coro, loop=cls.BASE.DiscordLoop)

    # send welcome message
    if Settings.welcome_chan and Settings.welcome_msg and (not link_in_name):

        WelcomeChan: discord.TextChannel = getDiscordChannelFromString(
            cls, Member.guild, Settings.welcome_chan, required_type="text")
        if WelcomeChan:
            welcome_msg_vars: dict = {
                "user-name": Member.name,
                "user-mention": Member.mention,
                "server-name": Member.guild.name,
                "member-count": str(Member.guild.member_count)
            }
            finished_message: str = await responseFormatter(
                cls,
                Settings.welcome_msg,
                var_dict=welcome_msg_vars,
                enable_special=True,
                DiscordGuild=Member.guild)
            try:
                finished_message = finished_message[:1997]
                await WelcomeChan.send(finished_message)
            except Exception as E:
                cls.BASE.Logger.warning(
                    f"Can't send welcome message: {E} {traceback.format_exc()}"
                )

    # send private welcome message
    if Settings.welcome_msg_priv:

        welcome_msg_priv_vars: dict = {
            "user-name": Member.name,
            "server-name": Member.guild.name,
            "member-count": str(Member.guild.member_count)
        }
        finished_message: str = await responseFormatter(
            cls,
            Settings.welcome_msg_priv,
            var_dict=welcome_msg_priv_vars,
            enable_special=True,
            DiscordGuild=Member.guild)
        try:
            finished_message = finished_message[:1997]
            await Member.send(finished_message)
        except Exception as E:
            cls.BASE.Logger.warning(
                f"Can't send private welcome message: {E} {traceback.format_exc()}"
            )

    # give member autorole
    if Settings.autorole_id:
        RoleToGive: discord.Role = getDiscordRoleFromString(
            cls, Member.guild, Settings.autorole_id)
        if RoleToGive and RoleToGive < Member.guild.me.top_role:  # there is a role found and phaaze can give this role
            try:
                await Member.add_roles(RoleToGive)
            except Exception as E:
                cls.BASE.Logger.warning(
                    f"Can't add role to member: {E} {traceback.format_exc()}")

    # set member active, if there was a known entry
    cls.BASE.PhaazeDB.updateQuery(table="discord_user",
                                  content={"on_server": 1},
                                  where="guild_id = %s AND member_id = %s",
                                  where_values=(str(Member.guild.id),
                                                str(Member.id)))
Esempio n. 13
0
async def apiDiscordConfigsEdit(cls: "PhaazebotWeb",
                                WebRequest: ExtendedRequest) -> Response:
    """
	Default url: /api/discord/configs/edit
	"""
    Data: WebRequestContent = WebRequestContent(WebRequest)
    await Data.load()

    # get required stuff
    Edit: StorageTransformer = StorageTransformer()
    Edit["guild_id"] = Data.getStr("guild_id", "", must_be_digit=True)

    # checks
    if not Edit["guild_id"]:
        return await cls.Tree.Api.errors.apiMissingData(
            cls, WebRequest, msg="missing or invalid 'guild_id'")

    PhaazeDiscord: "PhaazebotDiscord" = cls.BASE.Discord
    Guild: discord.Guild = discord.utils.get(PhaazeDiscord.guilds,
                                             id=int(Edit["guild_id"]))
    if not Guild:
        return await cls.Tree.Api.Discord.errors.apiDiscordGuildUnknown(
            cls, WebRequest)

    # get user info
    AuthDiscord: AuthDiscordWebUser = await authDiscordWebUser(cls, WebRequest)
    if not AuthDiscord.found:
        return await cls.Tree.Api.errors.apiMissingAuthorisation(
            cls, WebRequest)

    # get member
    CheckMember: discord.Member = Guild.get_member(
        int(AuthDiscord.User.user_id))
    if not CheckMember:
        return await cls.Tree.Api.Discord.errors.apiDiscordMemberNotFound(
            cls,
            WebRequest,
            guild_id=Edit["guild_id"],
            user_id=AuthDiscord.User.user_id)

    # check permissions
    # to edit configs, at least moderator rights are needed, (there can be options that require server only duh)
    if not (CheckMember.guild_permissions.administrator
            or CheckMember.guild_permissions.manage_guild):
        return await cls.Tree.Api.Discord.errors.apiDiscordMissingPermission(
            cls,
            WebRequest,
            guild_id=Edit["guild_id"],
            user_id=AuthDiscord.User.user_id)

    Configs: DiscordServerSettings = await getDiscordSeverSettings(
        PhaazeDiscord, origin=Edit["guild_id"], prevent_new=True)

    if not Configs:
        return await cls.Tree.Api.Discord.errors.apiDiscordGuildUnknown(
            cls, WebRequest, msg="Could not find configs for this guild")

    # check all update values
    update: dict = dict()

    Edit["autorole_id"] = Data.getStr("autorole_id",
                                      UNDEFINED,
                                      len_max=128,
                                      allow_none=True)
    if Edit["autorole_id"] != UNDEFINED:
        error: bool = False
        if not Edit["autorole_id"]:
            update["autorole_id"] = None
        else:
            Role: discord.Role = getDiscordRoleFromString(
                PhaazeDiscord, Guild, Edit["autorole_id"])
            if not Role:
                error = True
            elif Role >= Guild.me.top_role:
                return await cls.Tree.Api.errors.apiWrongData(
                    cls, WebRequest, msg=f"The Role `{Role.name}` is to high")
            else:
                update["autorole_id"] = str(Role.id)

        if error:
            return await cls.Tree.Api.errors.apiWrongData(
                cls,
                WebRequest,
                msg=
                f"{Edit['autorole_id']} could not be resolved as a valid discord role"
            )

    # blacklist_ban_links
    Edit["blacklist_ban_links"] = Data.getBool("blacklist_ban_links",
                                               UNDEFINED)
    if Edit["blacklist_ban_links"] != UNDEFINED:
        update["blacklist_ban_links"] = Edit["blacklist_ban_links"]

    # blacklist_punishment
    Edit["blacklist_punishment"] = Data.getStr("blacklist_punishment",
                                               UNDEFINED,
                                               len_max=32)
    if Edit["blacklist_punishment"] != UNDEFINED:
        Edit["blacklist_punishment"] = checkPunishmentString(
            Edit["blacklist_punishment"])
        update["blacklist_punishment"] = Edit["blacklist_punishment"]

    # currency_name
    Edit["currency_name"] = Data.getStr("currency_name",
                                        UNDEFINED,
                                        len_max=256,
                                        allow_none=True)
    if Edit["currency_name"] != UNDEFINED:
        if not Edit['currency_name']:
            update["currency_name"] = None
        else:
            update["currency_name"] = Edit["currency_name"]

    # currency_name_multi
    Edit["currency_name_multi"] = Data.getStr("currency_name_multi",
                                              UNDEFINED,
                                              len_max=256,
                                              allow_none=True)
    if Edit["currency_name_multi"] != UNDEFINED:
        if not Edit["currency_name_multi"]:
            update["currency_name_multi"] = None
        else:
            update["currency_name_multi"] = Edit["currency_name_multi"]

    # leave_chan
    Edit["leave_chan"] = Data.getStr("leave_chan",
                                     UNDEFINED,
                                     len_max=128,
                                     allow_none=True)
    if Edit["leave_chan"] != UNDEFINED:
        error: bool = False
        if not Edit["leave_chan"]:
            update["leave_chan"] = None
        else:
            Chan: discord.TextChannel = getDiscordChannelFromString(
                PhaazeDiscord, Guild, Edit["leave_chan"], required_type="text")
            if not Chan:
                error = True
            else:
                update["leave_chan"] = str(Chan.id)

        if error:
            return await cls.Tree.Api.errors.apiWrongData(
                cls,
                WebRequest,
                msg=
                f"'{Edit['leave_chan']}' could not be resolved as a valid discord text channel"
            )

    # leave_msg
    Edit["leave_msg"] = Data.getStr("leave_msg",
                                    UNDEFINED,
                                    len_max=1750,
                                    allow_none=True)
    if Edit["leave_msg"] != UNDEFINED:
        if not Edit["leave_msg"]:
            update["leave_msg"] = None
        else:
            update["leave_msg"] = Edit["leave_msg"]

    # level_custom_msg
    Edit["level_custom_msg"] = Data.getStr("level_custom_msg",
                                           UNDEFINED,
                                           len_max=1750,
                                           allow_none=True)
    if Edit["level_custom_msg"] != UNDEFINED:
        if not Edit["level_custom_msg"]:
            update["level_custom_msg"] = None
        else:
            update["level_custom_msg"] = Edit["level_custom_msg"]

    # level_announce_chan
    Edit["level_announce_chan"] = Data.getStr("level_announce_chan",
                                              UNDEFINED,
                                              len_max=128,
                                              allow_none=True)
    if Edit["level_announce_chan"] != UNDEFINED:
        error: bool = False
        if not Edit["level_announce_chan"]:
            update["level_announce_chan"] = None
        else:
            Chan: discord.TextChannel = getDiscordChannelFromString(
                PhaazeDiscord,
                Guild,
                Edit["level_announce_chan"],
                required_type="text")
            if not Chan:
                error = True
            else:
                update["level_announce_chan"] = str(Chan.id)

        if error:
            return await cls.Tree.Api.errors.apiWrongData(
                cls,
                WebRequest,
                msg=
                f"'{Edit['level_announce_chan']}' could not be resolved as a valid discord text channel"
            )

        update["level_announce_chan"] = Edit["level_announce_chan"]

    # owner_disable_level
    Edit["owner_disable_level"] = Data.getBool("owner_disable_level",
                                               UNDEFINED)
    if Edit["owner_disable_level"] != UNDEFINED:
        if not Guild.owner == CheckMember:
            return await cls.Tree.Api.Discord.errors.apiDiscordMissingPermission(
                cls,
                WebRequest,
                guild_id=Edit["guild_id"],
                user_id=AuthDiscord.User.user_id,
                msg="changing 'owner_disable_level' require server owner")
        update["owner_disable_level"] = Edit["owner_disable_level"]

    # owner_disable_normal
    Edit["owner_disable_normal"] = Data.getBool("owner_disable_normal",
                                                UNDEFINED)
    if Edit["owner_disable_normal"] != UNDEFINED:
        if not Guild.owner == CheckMember:
            return await cls.Tree.Api.Discord.errors.apiDiscordMissingPermission(
                cls,
                WebRequest,
                guild_id=Edit["guild_id"],
                user_id=AuthDiscord.User.user_id,
                msg="changing 'owner_disable_level' require server owner")
        update["owner_disable_normal"] = Edit["owner_disable_normal"]

    # owner_disable_regular
    Edit["owner_disable_regular"] = Data.getBool("owner_disable_regular",
                                                 UNDEFINED)
    if Edit["owner_disable_regular"] != UNDEFINED:
        if not Guild.owner == CheckMember:
            return await cls.Tree.Api.Discord.errors.apiDiscordMissingPermission(
                cls,
                WebRequest,
                guild_id=Edit["guild_id"],
                user_id=AuthDiscord.User.user_id,
                msg="changing 'owner_disable_level' require server owner")
        update["owner_disable_regular"] = Edit["owner_disable_regular"]

    # owner_disable_mod
    Edit["owner_disable_mod"] = Data.getBool("owner_disable_mod", UNDEFINED)
    if Edit["owner_disable_mod"] != UNDEFINED:
        if not Guild.owner == CheckMember:
            return await cls.Tree.Api.Discord.errors.apiDiscordMissingPermission(
                cls,
                WebRequest,
                guild_id=Edit["guild_id"],
                user_id=AuthDiscord.User.user_id,
                msg="changing 'owner_disable_level' require server owner")
        update["owner_disable_mod"] = Edit["owner_disable_mod"]

    # track_channel
    Edit["track_channel"] = Data.getStr("track_channel",
                                        UNDEFINED,
                                        len_max=128,
                                        allow_none=True)
    if Edit["track_channel"] != UNDEFINED:
        error: bool = False
        if not Edit["track_channel"]:
            update["track_channel"] = Edit["track_channel"]
        else:
            Chan: discord.TextChannel = getDiscordChannelFromString(
                PhaazeDiscord,
                Guild,
                Edit["track_channel"],
                required_type="text")
            if not Chan:
                error = True
            else:
                update["track_channel"] = str(Chan.id)

        if error:
            return await cls.Tree.Api.errors.apiWrongData(
                cls,
                WebRequest,
                msg=
                f"'{Edit['track_channel']}' could not be resolved as a valid discord text channel"
            )

    # track_value
    Edit["track_value"] = Data.getInt("track_value", UNDEFINED, min_x=0)
    if Edit["track_value"] != UNDEFINED:
        update["track_value"] = Edit["track_value"]

    # welcome_chan
    Edit["welcome_chan"] = Data.getStr("welcome_chan",
                                       UNDEFINED,
                                       len_max=128,
                                       allow_none=True)
    if Edit["welcome_chan"] != UNDEFINED:
        error: bool = False
        if not Edit["welcome_chan"]:
            update["welcome_chan"] = None
        else:
            Chan: discord.TextChannel = getDiscordChannelFromString(
                PhaazeDiscord,
                Guild,
                Edit["welcome_chan"],
                required_type="text")
            if not Chan:
                error = True
            else:
                update["welcome_chan"] = str(Chan.id)

        if error:
            return await cls.Tree.Api.errors.apiWrongData(
                cls,
                WebRequest,
                msg=
                f"'{Edit['welcome_chan']}' could not be resolved as a valid discord text channel"
            )

    # welcome_msg
    Edit["welcome_msg"] = Data.getStr("welcome_msg",
                                      UNDEFINED,
                                      len_max=1750,
                                      allow_none=True)
    if Edit["welcome_msg"] != UNDEFINED:
        if not Edit["welcome_msg"]:
            update["welcome_msg"] = None
        else:
            update["welcome_msg"] = Edit["welcome_msg"]

    # welcome_msg_priv
    Edit["welcome_msg_priv"] = Data.getStr("welcome_msg_priv",
                                           UNDEFINED,
                                           len_max=1750,
                                           allow_none=True)
    if Edit["welcome_msg_priv"] != UNDEFINED:
        if not Edit["welcome_msg_priv"]:
            update["welcome_msg_priv"] = None
        else:
            update["welcome_msg_priv"] = Edit["welcome_msg_priv"]

    if not update:
        return await cls.Tree.Api.errors.apiMissingData(
            cls, WebRequest, msg="No changes, please add at least one")

    cls.BASE.PhaazeDB.updateQuery(table="discord_setting",
                                  content=update,
                                  where="`discord_setting`.`guild_id` = %s",
                                  where_values=(Edit["guild_id"], ))

    # logging
    log_coro: Coroutine = loggingOnConfigEdit(PhaazeDiscord,
                                              Configs,
                                              Editor=CheckMember,
                                              changes=update)
    asyncio.ensure_future(log_coro, loop=cls.BASE.DiscordLoop)

    cls.BASE.Logger.debug(
        f"(API/Discord) Configs: {Edit['guild_id']=} updated",
        require="discord:configs")
    return cls.response(text=json.dumps(
        dict(msg="Configs: Updated", changes=update, status=200)),
                        content_type="application/json",
                        status=200)
Esempio n. 14
0
async def apiDiscordTwitchalertsCreate(
        cls: "PhaazebotWeb", WebRequest: ExtendedRequest) -> Response:
    """
	Default url: /api/discord/twitchalerts/create
	"""
    Data: WebRequestContent = WebRequestContent(WebRequest)
    await Data.load()

    # get required stuff
    Create: StorageTransformer = StorageTransformer()
    Create["guild_id"] = Data.getStr("guild_id", UNDEFINED, must_be_digit=True)
    Create["discord_channel_id"] = Data.getStr("discord_channel_id",
                                               UNDEFINED,
                                               must_be_digit=True)
    Create["twitch_channel"] = Data.getStr("twitch_channel", UNDEFINED)
    Create["custom_msg"] = Data.getStr("custom_msg",
                                       None,
                                       len_max=MAX_CONTENT_SIZE,
                                       allow_none=True)
    Create["suppress_gamechange"] = Data.getBool("suppress_gamechange", False)

    # checks
    if not Create["guild_id"]:
        return await cls.Tree.Api.errors.apiMissingData(
            cls, WebRequest, msg="missing or invalid 'guild_id'")

    if not Create["discord_channel_id"]:
        return await cls.Tree.Api.errors.apiMissingData(
            cls, WebRequest, msg="missing or invalid 'discord_channel_id'")

    if not Create["twitch_channel"]:
        return await cls.Tree.Api.errors.apiMissingData(
            cls, WebRequest, msg="missing or invalid 'twitch_channel'")

    # get/check discord
    PhaazeDiscord: "PhaazebotDiscord" = cls.BASE.Discord
    Guild: discord.Guild = discord.utils.get(PhaazeDiscord.guilds,
                                             id=int(Create["guild_id"]))
    if not Guild:
        return await cls.Tree.Api.Discord.errors.apiDiscordGuildUnknown(
            cls, WebRequest)

    TargetDiscordChannel: discord.TextChannel = getDiscordChannelFromString(
        PhaazeDiscord,
        Guild,
        Create["discord_channel_id"],
        required_type="text")
    if not TargetDiscordChannel:
        return await cls.Tree.Api.Discord.errors.apiDiscordChannelNotFound(
            cls, WebRequest, msg=f"Could not find a valid text channel")

    Match: re.Match = TwitchRe().ChannelLink.match(Create["twitch_channel"])
    if Match:
        Create["twitch_channel"] = Match.group("name")

    user_res: list = await getTwitchUsers(cls.BASE,
                                          item=Create["twitch_channel"],
                                          item_type="login")
    if not user_res:
        return await cls.Tree.Api.Twitch.errors.apiTwitchUserNotFound(
            cls, WebRequest, user_name=Create["twitch_channel"])

    FoundUser: TwitchUser = user_res.pop(0)

    # check if already exists and limits
    res: list = cls.BASE.PhaazeDB.selectQuery(
        """
		SELECT
			COUNT(*) AS `all`,
			SUM(
				CASE WHEN `discord_twitch_alert`.`twitch_channel_id` = %(twitch_channel_id)s
				THEN 1 ELSE 0 END
			) AS `twitch_channel_match`,
			SUM(
				CASE WHEN `discord_twitch_alert`.`discord_channel_id` = %(discord_channel_id)s
				THEN 1 ELSE 0 END
			) AS `discord_channel_match`,
			SUM(
				CASE WHEN `discord_twitch_alert`.`discord_channel_id` = %(discord_channel_id)s
					AND `discord_twitch_alert`.`twitch_channel_id` = %(twitch_channel_id)s
				THEN 1 ELSE 0 END
			) AS `both_match`
		FROM `discord_twitch_alert`
		WHERE `discord_twitch_alert`.`discord_guild_id` = %(discord_guild_id)s""",
        dict(discord_guild_id=Create["guild_id"],
             discord_channel_id=str(TargetDiscordChannel.id),
             twitch_channel_id=FoundUser.user_id))

    if res[0]["all"] == 0:
        res[0]["twitch_channel_match"] = 0
        res[0]["discord_channel_match"] = 0
        res[0]["both_match"] = 0

    if res[0]["twitch_channel_match"] >= MAX_SAME_TWITCH_ALERTS_PER_GUILD:
        return await cls.Tree.Api.Discord.Twitchalerts.errors.apiDiscordAlertSameTwitchChannelLimit(
            cls,
            WebRequest,
            limit=MAX_SAME_TWITCH_ALERTS_PER_GUILD,
            twitch_name=FoundUser.display_name)

    if res[0]["discord_channel_match"] >= 1:
        # nothing... for now
        pass

    if res[0]["both_match"] >= 1:
        return await cls.Tree.Api.Discord.Twitchalerts.errors.apiDiscordAlertExists(
            cls,
            WebRequest,
            twitch_name=FoundUser.login,
            discord_id=TargetDiscordChannel.id)

    # get user info
    AuthDiscord: AuthDiscordWebUser = await authDiscordWebUser(cls, WebRequest)
    if not AuthDiscord.found:
        return await cls.Tree.Api.errors.apiMissingAuthorisation(
            cls, WebRequest)

    # get member
    CheckMember: discord.Member = Guild.get_member(
        int(AuthDiscord.User.user_id))
    if not CheckMember:
        return await cls.Tree.Api.Discord.errors.apiDiscordMemberNotFound(
            cls,
            WebRequest,
            guild_id=Create["guild_id"],
            user_id=AuthDiscord.User.user_id)

    # check permissions
    if not (CheckMember.guild_permissions.administrator
            or CheckMember.guild_permissions.manage_guild):
        return await cls.Tree.Api.Discord.errors.apiDiscordMissingPermission(
            cls,
            WebRequest,
            guild_id=Create["guild_id"],
            user_id=AuthDiscord.User.user_id)

    cls.BASE.PhaazeDB.insertQuery(table="discord_twitch_alert",
                                  content={
                                      "discord_guild_id":
                                      Create["guild_id"],
                                      "discord_channel_id":
                                      str(TargetDiscordChannel.id),
                                      "twitch_channel_id":
                                      FoundUser.user_id,
                                      "custom_msg":
                                      Create["custom_msg"],
                                      "suppress_gamechange":
                                      Create["suppress_gamechange"]
                                  })

    # logging
    GuildSettings: DiscordServerSettings = await getDiscordSeverSettings(
        PhaazeDiscord, Create["guild_id"], prevent_new=True)
    log_coro: Coroutine = loggingOnTwitchalertCreate(
        PhaazeDiscord,
        GuildSettings,
        Creator=CheckMember,
        discord_channel=TargetDiscordChannel.name,
        twitch_channel=FoundUser.login)
    asyncio.ensure_future(log_coro, loop=cls.BASE.DiscordLoop)

    # some after work, with the new data
    await placeGatheredData(cls, FoundUser)
    cls.BASE.Logger.debug(
        f"(API/Discord) Twitchalert: {Create['guild_id']=} added {FoundUser.user_id=}",
        require="discord:alert")
    return cls.response(text=json.dumps(
        dict(msg="Twitchalert: Added new entry",
             entry=FoundUser.user_type,
             status=200)),
                        content_type="application/json",
                        status=200)
Esempio n. 15
0
async def loggingOnLevelEdit(cls: "PhaazebotDiscord",
                             Settings: DiscordServerSettings,
                             **kwargs) -> None:
    """
	Logs the event when someone edits a discordmember-level via web.
	If track option `Level.edit` is active, it will send a message to discord

	Required keywords:
	------------------
	* `Remover` - discord.Member
	* `changed_member_id` - str
	* `changes` - dict
	"""
    logging_signature: str = "Level.edit"
    Editor: discord.Member = kwargs["Editor"]
    changed_member_id: str = kwargs["changed_member_id"]
    changes: dict = kwargs["changes"]

    LevelMember: discord.Member = getDiscordMemberFromString(
        cls, Editor.guild, changed_member_id)
    level_member_name: str = LevelMember.name if LevelMember else "(Unknown)"

    new_log_id: int = cls.BASE.PhaazeDB.insertQuery(
        table="discord_log",
        content={
            "guild_id":
            Settings.server_id,
            "event_value":
            TRACK_OPTIONS[logging_signature],
            "initiator_id":
            str(Editor.id),
            "content":
            f"{Editor.name} edited the level stats of: {level_member_name} changes: {str(changes)}"
        })

    if not (TRACK_OPTIONS[logging_signature] & Settings.track_value):
        return  # track option not active, skip message to discord server

    TargetChannel: discord.TextChannel = getDiscordChannelFromString(
        cls, Editor.guild, Settings.track_channel, required_type="text")
    if not TargetChannel: return  # no channel found

    Emb: discord.Embed = discord.Embed(
        title=f"Log Event - [{logging_signature}]",
        description=f"{Editor.name} edited level stats",
        timestamp=datetime.datetime.now(),
        color=EVENT_COLOR_WARNING,
        url=makeWebAccessLink(cls, Editor.guild.id, new_log_id))
    Emb.set_thumbnail(url=Editor.avatar_url or Editor.default_avatar_url)
    Emb.add_field(name="Edited member:", value=level_member_name, inline=False)
    if changes.get("edited", False):
        Emb.add_field(
            name="Warning:",
            value="EXP got changed, this member now has a [EDITED] mark",
            inline=False)

    try:
        await TargetChannel.send(embed=Emb)
    except Exception as E:
        cls.BASE.Logger.warning(
            f"Can't log message: {E} {traceback.format_exc()}")