diff --git a/discordbot/titanembeds/database/guilds.py b/discordbot/titanembeds/database/guilds.py index 2ec3172..298eeb6 100644 --- a/discordbot/titanembeds/database/guilds.py +++ b/discordbot/titanembeds/database/guilds.py @@ -21,6 +21,9 @@ class Guilds(Base): invite_link = db.Column(db.String(255)) # Custom Discord Invite Link post_timeout = db.Column(db.Integer, nullable=False, server_default="5") # Seconds to elapse before another message can be posted from the widget max_message_length = db.Column(db.Integer, nullable=False, server_default="300") # Chars length the message should be before being rejected by the server + banned_words_enabled = db.Column(db.Boolean(), nullable=False, server_default="0") # If banned words are enforced + banned_words_global_included = db.Column(db.Boolean(), nullable=False, server_default="0") # Add global banned words to the list + banned_words = db.Column(db.Text(), nullable=False, server_default="[]") # JSON list of strings to block from sending def __init__(self, guild_id, name, roles, channels, webhooks, emojis, owner_id, icon): self.guild_id = guild_id diff --git a/webapp/alembic/versions/8e806bcb2228_add_banned_words_columns.py b/webapp/alembic/versions/8e806bcb2228_add_banned_words_columns.py new file mode 100644 index 0000000..2467123 --- /dev/null +++ b/webapp/alembic/versions/8e806bcb2228_add_banned_words_columns.py @@ -0,0 +1,32 @@ +"""Add banned words columns + +Revision ID: 8e806bcb2228 +Revises: 176d26252734 +Create Date: 2018-07-08 23:26:18.412175 + +""" + +# revision identifiers, used by Alembic. +revision = '8e806bcb2228' +down_revision = '176d26252734' +branch_labels = None +depends_on = None + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('guilds', sa.Column('banned_words', sa.Text(), server_default='[]', nullable=False)) + op.add_column('guilds', sa.Column('banned_words_enabled', sa.Boolean(), server_default='0', nullable=False)) + op.add_column('guilds', sa.Column('banned_words_global_included', sa.Boolean(), server_default='0', nullable=False)) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('guilds', 'banned_words_global_included') + op.drop_column('guilds', 'banned_words_enabled') + op.drop_column('guilds', 'banned_words') + # ### end Alembic commands ### diff --git a/webapp/titanembeds/app.py b/webapp/titanembeds/app.py index 3627991..9d5c607 100644 --- a/webapp/titanembeds/app.py +++ b/webapp/titanembeds/app.py @@ -75,6 +75,10 @@ def privacy(): def vote(): return render_template("discordbotsorg_vote.html.j2", referrer=request.args.get("referrer", None)) +@app.route("/global_banned_words") +def global_banned_words(): + return render_template("global_banned_words.html.j2") + @app.before_first_request def before_first_request(): discord_api.init_discordrest() diff --git a/webapp/titanembeds/blueprints/admin/admin.py b/webapp/titanembeds/blueprints/admin/admin.py index 3f70d3f..c6696a5 100644 --- a/webapp/titanembeds/blueprints/admin/admin.py +++ b/webapp/titanembeds/blueprints/admin/admin.py @@ -183,6 +183,9 @@ def administrate_guild(guild_id): "guest_icon": db_guild.guest_icon if db_guild.guest_icon != None else "", "post_timeout": db_guild.post_timeout, "max_message_length": db_guild.max_message_length, + "banned_words_enabled": db_guild.banned_words_enabled, + "banned_words_global_included": db_guild.banned_words_global_included, + "banned_words": json.loads(db_guild.banned_words), } return render_template("administrate_guild.html.j2", guild=dbguild_dict, members=users, permissions=permissions, cosmetics=cosmetics) @@ -199,6 +202,8 @@ def update_administrate_guild(guild_id): db_guild.unauth_captcha = request.form.get("unauth_captcha", db_guild.unauth_captcha) in ["true", True] db_guild.post_timeout = request.form.get("post_timeout", db_guild.post_timeout) db_guild.max_message_length = request.form.get("max_message_length", db_guild.max_message_length) + db_guild.banned_words_enabled = request.form.get("banned_words_enabled", db_guild.banned_words_enabled) in ["true", True] + db_guild.banned_words_global_included = request.form.get("banned_words_global_included", db_guild.banned_words_global_included) in ["true", True] invite_link = request.form.get("invite_link", db_guild.invite_link) if invite_link != None and invite_link.strip() == "": invite_link = None @@ -207,6 +212,15 @@ def update_administrate_guild(guild_id): if guest_icon != None and guest_icon.strip() == "": guest_icon = None db_guild.guest_icon = guest_icon + banned_word = request.form.get("banned_word", None) + if banned_word: + delete_banned_word = request.form.get("delete_banned_word", False) in ["true", True] + banned_words = set(json.loads(db_guild.banned_words)) + if delete_banned_word: + banned_words.discard(banned_word) + else: + banned_words.add(banned_word) + db_guild.banned_words = json.dumps(list(banned_words)) db.session.commit() emit("guest_icon_change", {"guest_icon": guest_icon if guest_icon else url_for('static', filename='img/titanembeds_square.png')}, room="GUILD_"+guild_id, namespace="/gateway") return jsonify( @@ -222,6 +236,9 @@ def update_administrate_guild(guild_id): unauth_captcha=db_guild.unauth_captcha, post_timeout=db_guild.post_timeout, max_message_length=db_guild.max_message_length, + banned_words_enabled=db_guild.banned_words_enabled, + banned_words_global_included=db_guild.banned_words_global_included, + banned_words=json.loads(db_guild.banned_words), ) @admin.route("/guilds") diff --git a/webapp/titanembeds/blueprints/api/api.py b/webapp/titanembeds/blueprints/api/api.py index 97d4084..fc24e3e 100644 --- a/webapp/titanembeds/blueprints/api/api.py +++ b/webapp/titanembeds/blueprints/api/api.py @@ -2,6 +2,7 @@ from titanembeds.database import db, Guilds, UnauthenticatedUsers, Unauthenticat from titanembeds.decorators import valid_session_required, discord_users_only, abort_if_guild_disabled from titanembeds.utils import check_guild_existance, guild_accepts_visitors, guild_query_unauth_users_bool, get_client_ipaddr, discord_api, rate_limiter, channel_ratelimit_key, guild_ratelimit_key, user_unauthenticated, checkUserRevoke, checkUserBanned, update_user_status, check_user_in_guild, get_guild_channels, guild_webhooks_enabled, guild_unauthcaptcha_enabled, get_member_roles, get_online_embed_user_keys, redis_store from titanembeds.oauth import user_has_permission, generate_avatar_url, check_user_can_administrate_guild +import titanembeds.constants as constants from flask import Blueprint, abort, jsonify, session, request, url_for from flask import current_app as app from flask_socketio import emit @@ -61,6 +62,17 @@ def format_post_content(guild_id, channel_id, message, dbUser): mention = "<@" + match[2: len(match) - 1] + ">" message = message.replace(match, mention, 1) + if dbguild.banned_words_enabled: + banned_words = set(json.loads(dbguild.banned_words)) + if dbguild.banned_words_global_included: + banned_words = banned_words.union(set(constants.GLOBAL_BANNED_WORDS)) + for word in banned_words: + word_boundaried = r"\b%s\b" % word + regex = re.compile(word_boundaried, re.IGNORECASE) + if regex.match(message): + illegal_post = True + illegal_reasons.append("The following word is prohibited: " + word) + if not guild_webhooks_enabled(guild_id): if (session['unauthenticated']): message = u"**[{}#{}]** {}".format(session['username'], session['user_id'], message) diff --git a/webapp/titanembeds/blueprints/user/user.py b/webapp/titanembeds/blueprints/user/user.py index 1ccb188..7d366e7 100644 --- a/webapp/titanembeds/blueprints/user/user.py +++ b/webapp/titanembeds/blueprints/user/user.py @@ -219,6 +219,9 @@ def administrate_guild(guild_id): "guest_icon": db_guild.guest_icon if db_guild.guest_icon != None else "", "post_timeout": db_guild.post_timeout, "max_message_length": db_guild.max_message_length, + "banned_words_enabled": db_guild.banned_words_enabled, + "banned_words_global_included": db_guild.banned_words_global_included, + "banned_words": json.loads(db_guild.banned_words), } return render_template("administrate_guild.html.j2", guild=dbguild_dict, members=users, permissions=permissions, cosmetics=cosmetics, disabled=(guild_id in list_disabled_guilds())) @@ -243,6 +246,8 @@ def update_administrate_guild(guild_id): db_guild.unauth_captcha = request.form.get("unauth_captcha", db_guild.unauth_captcha) in ["true", True] db_guild.post_timeout = request.form.get("post_timeout", db_guild.post_timeout) db_guild.max_message_length = request.form.get("max_message_length", db_guild.max_message_length) + db_guild.banned_words_enabled = request.form.get("banned_words_enabled", db_guild.banned_words_enabled) in ["true", True] + db_guild.banned_words_global_included = request.form.get("banned_words_global_included", db_guild.banned_words_global_included) in ["true", True] invite_link = request.form.get("invite_link", db_guild.invite_link) if invite_link != None and invite_link.strip() == "": @@ -254,6 +259,16 @@ def update_administrate_guild(guild_id): guest_icon = None db_guild.guest_icon = guest_icon + banned_word = request.form.get("banned_word", None) + if banned_word: + delete_banned_word = request.form.get("delete_banned_word", False) in ["true", True] + banned_words = set(json.loads(db_guild.banned_words)) + if delete_banned_word: + banned_words.discard(banned_word) + else: + banned_words.add(banned_word) + db_guild.banned_words = json.dumps(list(banned_words)) + db.session.commit() emit("guest_icon_change", {"guest_icon": guest_icon if guest_icon else url_for('static', filename='img/titanembeds_square.png')}, room="GUILD_"+guild_id, namespace="/gateway") return jsonify( @@ -269,6 +284,9 @@ def update_administrate_guild(guild_id): unauth_captcha=db_guild.unauth_captcha, post_timeout=db_guild.post_timeout, max_message_length=db_guild.max_message_length, + banned_words_enabled=db_guild.banned_words_enabled, + banned_words_global_included=db_guild.banned_words_global_included, + banned_words=json.loads(db_guild.banned_words), ) @user.route("/add-bot/") diff --git a/webapp/titanembeds/constants.py b/webapp/titanembeds/constants.py index 257598b..a18aa68 100644 --- a/webapp/titanembeds/constants.py +++ b/webapp/titanembeds/constants.py @@ -427,3 +427,415 @@ LANGUAGES = [ ], }, ] + +# Original list adopted from https://github.com/areebbeigh/profanityfilter/blob/master/profanityfilter/data/badwords.txt +GLOBAL_BANNED_WORDS = [ + "@$$", + "ahole", + "amcik", + "andskota", + "anus", + "arschloch", + "arse", + "ash0le", + "ash0les", + "asholes", + "ass", + "assface", + "assh0le", + "assh0lez", + "asshole", + "assholes", + "assholz", + "assmonkey", + "assrammer", + "asswipe", + "ayir", + "azzhole", + "b00bs", + "b17ch", + "b1tch", + "bassterds", + "bastard", + "bastards", + "bastardz", + "basterds", + "basterdz", + "bch", + "bi7ch", + "biatch", + "bich", + "bitch", + "bitches", + "blowjob", + "boffing", + "boiolas", + "bollock", + "boobs", + "breasts", + "btch", + "buceta", + "bullshit", + "butthole", + "buttpirate", + "buttwipe", + "c0ck", + "c0cks", + "c0k", + "cabron", + "carpetmuncher", + "cawk", + "cawks", + "cazzo", + "chink", + "chraa", + "chuj", + "cipa", + "clit", + "clits", + "cnts", + "cntz", + "cock", + "cockhead", + "cocks", + "cocksucker", + "crap", + "cum", + "cunt", + "cunts", + "cuntz", + "d4mn", + "damn", + "daygo", + "dego", + "dick", + "dike", + "dild0", + "dild0s", + "dildo", + "dildos", + "dilld0", + "dilld0s", + "dirsa", + "dominatricks", + "dominatrics", + "dominatrix", + "dupa", + "dyke", + "dziwka", + "ejackulate", + "ejakulate", + "ekrem", + "ekto", + "enculer", + "enema", + "faen", + "fag", + "fag1t", + "faget", + "fagg1t", + "faggit", + "faggot", + "fagit", + "fags", + "fagz", + "faig", + "faigs", + "fanculo", + "fanny", + "fart", + "fatass", + "fcuk", + "feces", + "feg", + "felcher", + "ficken", + "fitt", + "flikker", + "flipping", + "foreskin", + "fotze", + "fu", + "fuchah", + "fuck", + "fucka", + "fucker", + "fuckin", + "fucking", + "fucks", + "fudgepacker", + "fuk", + "fukah", + "fuken", + "fuker", + "fukin", + "fukk", + "fukka", + "fukkah", + "fukken", + "fukker", + "fukkin", + "futkretzn", + "fux0r", + "g00k", + "gay", + "gaybor", + "gayboy", + "gaygirl", + "gays", + "gayz", + "goddamned", + "gook", + "guiena", + "h00r", + "h0ar", + "h0r", + "h0re", + "h4x0r", + "hell", + "hells", + "helvete", + "hoar", + "hoer", + "honkey", + "hoor", + "hoore", + "hore", + "huevon", + "hui", + "injun", + "jackoff", + "jap", + "japs", + "jerkoff", + "jisim", + "jism", + "jiss", + "jizm", + "jizz", + "kanker", + "kawk", + "kike", + "klootzak", + "knob", + "knobs", + "knobz", + "knulle", + "kraut", + "kuk", + "kuksuger", + "kunt", + "kunts", + "kuntz", + "kurac", + "kurwa", + "kusi", + "kyrpa", + "l3i+ch", + "l3itch", + "lesbian", + "lesbo", + "lezzian", + "lipshits", + "lipshitz", + "mamhoon", + "masochist", + "masokist", + "massterbait", + "masstrbait", + "masstrbate", + "masterbaiter", + "masterbat", + "masterbat3", + "masterbate", + "masterbates", + "masturbat", + "masturbate", + "merd", + "mibun", + "mofo", + "monkleigh", + "motha", + "mothafucker", + "mothafuker", + "mothafukkah", + "mothafukker", + "motherfucker", + "motherfukah", + "motherfuker", + "motherfukkah", + "motherfukker", + "mouliewop", + "muie", + "mulkku", + "muschi", + "mutha", + "muthafucker", + "muthafukah", + "muthafuker", + "muthafukkah", + "muthafukker", + "n1gr", + "nastt", + "nasty", + "nazi", + "nazis", + "nepesaurio", + "nigga", + "niggas", + "nigger", + "nigur", + "niiger", + "niigr", + "nutsack", + "orafis", + "orgasim", + "orgasm", + "orgasum", + "oriface", + "orifice", + "orifiss", + "orospu", + "p0rn", + "packi", + "packie", + "packy", + "paki", + "pakie", + "paky", + "paska", + "pecker", + "peeenus", + "peeenusss", + "peenus", + "peinus", + "pen1s", + "penas", + "penis", + "penisbreath", + "penus", + "penuus", + "perse", + "phuc", + "phuck", + "phuk", + "phuker", + "phukker", + "picka", + "pierdol", + "pillu", + "pimmel", + "pimpis", + "piss", + "pizda", + "polac", + "polack", + "polak", + "poonani", + "poontsee", + "poop", + "porn", + "pr0n", + "pr1c", + "pr1ck", + "pr1k", + "preteen", + "pula", + "pule", + "pusse", + "pussee", + "pussy", + "puta", + "puto", + "puuke", + "puuker", + "qahbeh", + "queef", + "queer", + "queers", + "queerz", + "qweers", + "qweerz", + "qweir", + "rautenberg", + "recktum", + "rectum", + "retard", + "s.o.b.", + "sadist", + "scank", + "schaffer", + "scheiss", + "schlampe", + "schlong", + "schmuck", + "screw", + "screwing", + "scrotum", + "semen", + "sex", + "sexx", + "sexxx", + "sexy", + "sh1t", + "sh1ter", + "sh1ts", + "sh1tter", + "sh1tz", + "sharmuta", + "sharmute", + "shemale", + "shi+", + "shipal", + "shit", + "shits", + "shitt", + "shitter", + "shitty", + "shity", + "shitz", + "shiz", + "sht", + "shyt", + "shyte", + "shytty", + "skanck", + "skank", + "skankee", + "skankey", + "skanks", + "skanky", + "skrib", + "slut", + "sluts", + "slutty", + "slutz", + "smut", + "sonofabitch", + "sx", + "teets", + "teez", + "testical", + "testicle", + "tit", + "tits", + "titt", + "turd", + "va1jina", + "vag1na", + "vagiina", + "vagina", + "vaj1na", + "vajina", + "vullva", + "vulva", + "w00se", + "w0p", + "wank", + "wh00r", + "wh0re", + "whoar", + "whore", + "xrated", + "xxx", +] \ No newline at end of file diff --git a/webapp/titanembeds/database/guilds.py b/webapp/titanembeds/database/guilds.py index 32b496c..3abf9c1 100644 --- a/webapp/titanembeds/database/guilds.py +++ b/webapp/titanembeds/database/guilds.py @@ -21,6 +21,9 @@ class Guilds(db.Model): invite_link = db.Column(db.String(255)) # Custom Discord Invite Link post_timeout = db.Column(db.Integer, nullable=False, server_default="5") # Seconds to elapse before another message can be posted from the widget max_message_length = db.Column(db.Integer, nullable=False, server_default="300") # Chars length the message should be before being rejected by the server + banned_words_enabled = db.Column(db.Boolean(), nullable=False, server_default="0") # If banned words are enforced + banned_words_global_included = db.Column(db.Boolean(), nullable=False, server_default="0") # Add global banned words to the list + banned_words = db.Column(db.Text(), nullable=False, server_default="[]") # JSON list of strings to block from sending def __init__(self, guild_id, name, roles, channels, webhooks, emojis, owner_id, icon): self.guild_id = guild_id diff --git a/webapp/titanembeds/static/js/administrate_guild.js b/webapp/titanembeds/static/js/administrate_guild.js index 0ca8bef..fba3c30 100644 --- a/webapp/titanembeds/static/js/administrate_guild.js +++ b/webapp/titanembeds/static/js/administrate_guild.js @@ -1,3 +1,5 @@ +$('.chips').material_chip(); + $('#unauth_users').change(function() { var pathname = window.location.pathname; var checked = $(this).is(':checked') @@ -107,6 +109,51 @@ $('#unauth_captcha').change(function() { }); }); +$('#banned_words_enabled').change(function() { + var pathname = window.location.pathname; + var checked = $(this).is(':checked') + var payload = {"banned_words_enabled": checked} + $.post(pathname, payload, function(data) { + Materialize.toast('Updated Banned Words setting!', 2000) + }); +}); + +$('#banned_words_global_included').change(function() { + var pathname = window.location.pathname; + var checked = $(this).is(':checked') + var payload = {"banned_words_global_included": checked} + $.post(pathname, payload, function(data) { + Materialize.toast('Updated Banned Words Global setting!', 2000) + }); +}); + +var banned_words_data = []; +for (var i = 0; i < BANNED_WORDS.length; i++) { + banned_words_data.push({ + tag: BANNED_WORDS[i] + }); +} + +$('#banned_words').material_chip({ + data: banned_words_data, +}); + +$('#banned_words').on('chip.add', function(e, chip){ + add_delete_banned_words("add", chip.tag); +}); + +$('#banned_words').on('chip.delete', function(e, chip){ + add_delete_banned_words("delete", chip.tag); +}); + +function add_delete_banned_words(action, word) { + var pathname = window.location.pathname; + var payload = {"banned_word": word, "delete_banned_word": action == "delete"} + $.post(pathname, payload, function(data) { + Materialize.toast('Updated Banned Words list!', 2000) + }); +} + function initiate_ban(guild_id, user_id) { var reason = prompt("Please enter your reason for ban"); var payload = { diff --git a/webapp/titanembeds/templates/administrate_guild.html.j2 b/webapp/titanembeds/templates/administrate_guild.html.j2 index 7f7dbab..7912d0b 100644 --- a/webapp/titanembeds/templates/administrate_guild.html.j2 +++ b/webapp/titanembeds/templates/administrate_guild.html.j2 @@ -161,6 +161,31 @@

Your user account does not have access to change guest avatar url. Please visit the Titan Tokens shop to activate this cosmetic item.

{% endif %} + +
+ +

Banned Words + + + +

+

Prevent users from sending messages from the embed containing these words.

+
+ Include Global Banned Words to the list + +
+

(Hit enter after each word to add to list)

+
@@ -240,5 +265,8 @@ {% endblock %} {% block script %} + {% endblock %} diff --git a/webapp/titanembeds/templates/global_banned_words.html.j2 b/webapp/titanembeds/templates/global_banned_words.html.j2 new file mode 100644 index 0000000..5b3b8d0 --- /dev/null +++ b/webapp/titanembeds/templates/global_banned_words.html.j2 @@ -0,0 +1,25 @@ +{% extends 'site_layout.html.j2' %} +{% set title="Global Banned Words" %} + +{% block content %} +

Global Banned Words

+

These following words are predefined as the global banned words for those who want a sqeaky clean server.

+
+
+
+
+
+

Upon enforcing banned words along with the global banned words (both are enabled in your server dashboard), any messages sent via the embeds that contain these words will be prevented to be sent.

+

If you have any suggestions on adding more profane words to the global list, feel free to open a pull request and modify this file.

+
+
    + {% for word in constants.GLOBAL_BANNED_WORDS %} +
  1. {{ word }}
  2. + {% endfor %} +
+
+
+
+
+
+{% endblock %}