Add the ability to disallow words (banned words)

This commit is contained in:
Jeremy Zhang 2018-07-09 01:37:35 +00:00
parent f0f1f226e3
commit ccf9aed9ec
11 changed files with 601 additions and 0 deletions

View File

@ -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

View File

@ -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 ###

View File

@ -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()

View File

@ -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")

View File

@ -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)

View File

@ -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/<guild_id>")

View File

@ -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",
]

View File

@ -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

View File

@ -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 = {

View File

@ -161,6 +161,31 @@
<p class="red lighten-4"><strong>Your user account does not have access to change guest avatar url. Please visit the Titan Tokens shop to activate this cosmetic item.</strong></p>
{% endif %}
<input id="guest_icon" value="{{ guild['guest_icon'] }}" {% if not cosmetics.guest_icon %}disabled{% endif %}>
<br>
<p class="flow-text">Banned Words
<span class="switch">
<label>
Disable
<input type="checkbox" id="banned_words_enabled" name="banned_words_enabled" {% if guild['banned_words_enabled'] %}checked{% endif %} >
<span class="lever"></span>
Enable
</label>
</span>
</p>
<p>Prevent users from sending messages from the embed containing these words.</p>
<div class="switch">
<span>Include <a href="{{ url_for("global_banned_words") }}" target="_blank">Global Banned Words</a> to the list</span>
<label>
Disable
<input type="checkbox" id="banned_words_global_included" name="banned_words_global_included" {% if guild['banned_words_global_included'] %}checked{% endif %} >
<span class="lever"></span>
Enable
</label>
</div>
<p>(Hit enter after each word to add to list)</p>
<div id="banned_words" class="chips"></div>
</div>
</div>
</div>
@ -240,5 +265,8 @@
{% endblock %}
{% block script %}
<script>
const BANNED_WORDS = {{ guild["banned_words"]|tojson|safe }};
</script>
<script type="text/javascript" src="{{ url_for('static', filename='js/administrate_guild.js') }}"></script>
{% endblock %}

View File

@ -0,0 +1,25 @@
{% extends 'site_layout.html.j2' %}
{% set title="Global Banned Words" %}
{% block content %}
<h1>Global Banned Words</h1>
<p class="flow-text">These following words are predefined as the global banned words for those who want a sqeaky clean server.</p>
<div class="row">
<div class="col s12">
<div class="card horizontal black-text indigo lighten-5 z-depth-3 hoverable">
<div class="card-stacked">
<div class="card-content">
<p>Upon enforcing banned words along with the <strong>global</strong> 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.</p>
<p>If you have any suggestions on adding more profane words to the global list, feel free to open a pull request and modify <a href="https://github.com/TitanEmbeds/Titan/blob/master/webapp/titanembeds/constants.py" target="_blank">this file.</a></p>
<hr>
<ol>
{% for word in constants.GLOBAL_BANNED_WORDS %}
<li>{{ word }}</li>
{% endfor %}
</ol>
</div>
</div>
</div>
</div>
</div>
{% endblock %}