diff --git a/discordbot/titanembeds/database/guilds.py b/discordbot/titanembeds/database/guilds.py index b391b74..06f0040 100644 --- a/discordbot/titanembeds/database/guilds.py +++ b/discordbot/titanembeds/database/guilds.py @@ -7,6 +7,7 @@ class Guilds(Base): name = db.Column(db.String(255)) # Name unauth_users = db.Column(db.Boolean()) # If allowed unauth users visitor_view = db.Column(db.Boolean()) # If users are automatically "signed in" and can view chat + webhook_messages = db.Column(db.Boolean()) # Use webhooks to send messages instead of the bot chat_links = db.Column(db.Boolean()) # If users can post links bracket_links = db.Column(db.Boolean()) # If appending brackets to links to prevent embed mentions_limit = db.Column(db.Integer) # If there is a limit on the number of mentions in a msg @@ -23,6 +24,7 @@ class Guilds(Base): self.name = name self.unauth_users = True # defaults to true self.visitor_view = False + self.webhook_messages = False self.chat_links = True self.bracket_links = True self.mentions_limit = -1 # -1 = unlimited mentions diff --git a/webapp/alembic/versions/dadcb876cdd9_added_webhook_messages_boolean_column_.py b/webapp/alembic/versions/dadcb876cdd9_added_webhook_messages_boolean_column_.py new file mode 100644 index 0000000..f51ad78 --- /dev/null +++ b/webapp/alembic/versions/dadcb876cdd9_added_webhook_messages_boolean_column_.py @@ -0,0 +1,144 @@ +"""Added webhook messages boolean column to guilds + +Revision ID: dadcb876cdd9 +Revises: 2a2f32ac91d6 +Create Date: 2017-08-27 20:01:30.874376 + +""" + +# revision identifiers, used by Alembic. +revision = 'dadcb876cdd9' +down_revision = '2a2f32ac91d6' +branch_labels = None +depends_on = None + +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import mysql + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('cosmetics', 'css', + existing_type=mysql.TINYINT(display_width=1), + type_=sa.Boolean(), + existing_nullable=False) + op.alter_column('guild_members', 'active', + existing_type=mysql.TINYINT(display_width=1), + type_=sa.Boolean(), + existing_nullable=False, + existing_server_default=sa.text("'1'")) + op.alter_column('guild_members', 'banned', + existing_type=mysql.TINYINT(display_width=1), + type_=sa.Boolean(), + existing_nullable=False, + existing_server_default=sa.text("'0'")) + op.add_column('guilds', sa.Column('webhook_messages', sa.Boolean(), nullable=False)) + op.alter_column('guilds', 'bracket_links', + existing_type=mysql.TINYINT(display_width=1), + type_=sa.Boolean(), + existing_nullable=False, + existing_server_default=sa.text("'1'")) + op.alter_column('guilds', 'channels', + existing_type=mysql.LONGTEXT(collation='utf8mb4_unicode_ci'), + type_=sa.Text(length=4294967295), + existing_nullable=False) + op.alter_column('guilds', 'chat_links', + existing_type=mysql.TINYINT(display_width=1), + type_=sa.Boolean(), + existing_nullable=False, + existing_server_default=sa.text("'1'")) + op.alter_column('guilds', 'emojis', + existing_type=mysql.LONGTEXT(collation='utf8mb4_unicode_ci'), + type_=sa.Text(length=4294967295), + existing_nullable=False) + op.alter_column('guilds', 'roles', + existing_type=mysql.LONGTEXT(collation='utf8mb4_unicode_ci'), + type_=sa.Text(length=4294967295), + existing_nullable=False) + op.alter_column('guilds', 'unauth_users', + existing_type=mysql.TINYINT(display_width=1), + type_=sa.Boolean(), + existing_nullable=False, + existing_server_default=sa.text("'1'")) + op.alter_column('guilds', 'visitor_view', + existing_type=mysql.TINYINT(display_width=1), + type_=sa.Boolean(), + existing_nullable=False) + op.alter_column('guilds', 'webhooks', + existing_type=mysql.LONGTEXT(collation='utf8mb4_unicode_ci'), + type_=sa.Text(length=4294967295), + existing_nullable=False) + op.alter_column('unauthenticated_users', 'revoked', + existing_type=mysql.TINYINT(display_width=1), + type_=sa.Boolean(), + existing_nullable=False, + existing_server_default=sa.text("'0'")) + op.alter_column('user_css', 'css', + existing_type=mysql.LONGTEXT(collation='utf8mb4_unicode_ci'), + type_=sa.Text(length=4294967295), + existing_nullable=True) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('user_css', 'css', + existing_type=sa.Text(length=4294967295), + type_=mysql.LONGTEXT(collation='utf8mb4_unicode_ci'), + existing_nullable=True) + op.alter_column('unauthenticated_users', 'revoked', + existing_type=sa.Boolean(), + type_=mysql.TINYINT(display_width=1), + existing_nullable=False, + existing_server_default=sa.text("'0'")) + op.alter_column('guilds', 'webhooks', + existing_type=sa.Text(length=4294967295), + type_=mysql.LONGTEXT(collation='utf8mb4_unicode_ci'), + existing_nullable=False) + op.alter_column('guilds', 'visitor_view', + existing_type=sa.Boolean(), + type_=mysql.TINYINT(display_width=1), + existing_nullable=False) + op.alter_column('guilds', 'unauth_users', + existing_type=sa.Boolean(), + type_=mysql.TINYINT(display_width=1), + existing_nullable=False, + existing_server_default=sa.text("'1'")) + op.alter_column('guilds', 'roles', + existing_type=sa.Text(length=4294967295), + type_=mysql.LONGTEXT(collation='utf8mb4_unicode_ci'), + existing_nullable=False) + op.alter_column('guilds', 'emojis', + existing_type=sa.Text(length=4294967295), + type_=mysql.LONGTEXT(collation='utf8mb4_unicode_ci'), + existing_nullable=False) + op.alter_column('guilds', 'chat_links', + existing_type=sa.Boolean(), + type_=mysql.TINYINT(display_width=1), + existing_nullable=False, + existing_server_default=sa.text("'1'")) + op.alter_column('guilds', 'channels', + existing_type=sa.Text(length=4294967295), + type_=mysql.LONGTEXT(collation='utf8mb4_unicode_ci'), + existing_nullable=False) + op.alter_column('guilds', 'bracket_links', + existing_type=sa.Boolean(), + type_=mysql.TINYINT(display_width=1), + existing_nullable=False, + existing_server_default=sa.text("'1'")) + op.drop_column('guilds', 'webhook_messages') + op.alter_column('guild_members', 'banned', + existing_type=sa.Boolean(), + type_=mysql.TINYINT(display_width=1), + existing_nullable=False, + existing_server_default=sa.text("'0'")) + op.alter_column('guild_members', 'active', + existing_type=sa.Boolean(), + type_=mysql.TINYINT(display_width=1), + existing_nullable=False, + existing_server_default=sa.text("'1'")) + op.alter_column('cosmetics', 'css', + existing_type=sa.Boolean(), + type_=mysql.TINYINT(display_width=1), + existing_nullable=False) + # ### end Alembic commands ### diff --git a/webapp/titanembeds/blueprints/admin/admin.py b/webapp/titanembeds/blueprints/admin/admin.py index 22712c9..e773718 100644 --- a/webapp/titanembeds/blueprints/admin/admin.py +++ b/webapp/titanembeds/blueprints/admin/admin.py @@ -135,6 +135,7 @@ def administrate_guild(guild_id): "name": db_guild.name, "unauth_users": db_guild.unauth_users, "visitor_view": db_guild.visitor_view, + "webhook_messages": db_guild.webhook_messages, "chat_links": db_guild.chat_links, "bracket_links": db_guild.bracket_links, "mentions_limit": db_guild.mentions_limit, @@ -149,6 +150,7 @@ def update_administrate_guild(guild_id): db_guild = db.session.query(Guilds).filter(Guilds.guild_id == guild_id).first() db_guild.unauth_users = request.form.get("unauth_users", db_guild.unauth_users) in ["true", True] db_guild.visitor_view = request.form.get("visitor_view", db_guild.visitor_view) in ["true", True] + db_guild.webhook_messages = request.form.get("webhook_messages", db_guild.webhook_messages) in ["true", True] db_guild.chat_links = request.form.get("chat_links", db_guild.chat_links) in ["true", True] db_guild.bracket_links = request.form.get("bracket_links", db_guild.bracket_links) in ["true", True] db_guild.mentions_limit = request.form.get("mentions_limit", db_guild.mentions_limit) @@ -161,6 +163,8 @@ def update_administrate_guild(guild_id): id=db_guild.id, guild_id=db_guild.guild_id, unauth_users=db_guild.unauth_users, + visitor_view=db_guild.visitor_view, + webhook_messages=db_guild.webhook_messages, chat_links=db_guild.chat_links, bracket_links=db_guild.bracket_links, mentions_limit=db_guild.mentions_limit, diff --git a/webapp/titanembeds/blueprints/api/api.py b/webapp/titanembeds/blueprints/api/api.py index 2002dae..f28d4e1 100644 --- a/webapp/titanembeds/blueprints/api/api.py +++ b/webapp/titanembeds/blueprints/api/api.py @@ -2,7 +2,7 @@ from titanembeds.database import db, Guilds, UnauthenticatedUsers, Unauthenticat from titanembeds.decorators import valid_session_required, discord_users_only 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 from titanembeds.oauth import user_has_permission, generate_avatar_url, check_user_can_administrate_guild -from titanembeds.userbookkeeping import user_unauthenticated, checkUserRevoke, checkUserBanned, update_user_status, check_user_in_guild, get_guild_channels +from titanembeds.userbookkeeping import user_unauthenticated, checkUserRevoke, checkUserBanned, update_user_status, check_user_in_guild, get_guild_channels, guild_webhooks_enabled from flask import Blueprint, abort, jsonify, session, request, url_for from flask_socketio import emit from sqlalchemy import and_ @@ -50,7 +50,7 @@ def format_post_content(guild_id, channel_id, message, dbUser): mention = "<@" + match[2: len(match) - 1] + ">" message = message.replace(match, mention, 1) - if not get_channel_webhook_url(guild_id, channel_id): + if not guild_webhooks_enabled(guild_id): if (session['unauthenticated']): message = u"**[{}#{}]** {}".format(session['username'], session['user_id'], message) else: @@ -137,15 +137,23 @@ def get_guild_emojis(guild_id): # Returns webhook url if exists and can post w/webhooks, otherwise None def get_channel_webhook_url(guild_id, channel_id): + if not guild_webhooks_enabled(guild_id): + return None dbguild = db.session.query(Guilds).filter(Guilds.guild_id == guild_id).first() guild_webhooks = json.loads(dbguild.webhooks) + name = "[Titan] " + if user_unauthenticated(): + name = name + session["username"] + "#" + str(session["user_id"]) + else: + name = name + session["username"] + "#" + str(session["discriminator"]) for webhook in guild_webhooks: - if channel_id == webhook["channel_id"] and (webhook["name"].lower().startswith("titan") or webhook["name"].lower().startswith("[titan]")): + if channel_id == webhook["channel_id"] and webhook["name"] == name: return { "id": webhook["id"], "token": webhook["token"] } - return None + webhook = discord_api.create_webhook(channel_id, name) + return webhook["content"] @api.route("/fetch", methods=["GET"]) @valid_session_required(api=True) diff --git a/webapp/titanembeds/blueprints/gateway/gateway.py b/webapp/titanembeds/blueprints/gateway/gateway.py index 946483e..57ca873 100644 --- a/webapp/titanembeds/blueprints/gateway/gateway.py +++ b/webapp/titanembeds/blueprints/gateway/gateway.py @@ -1,10 +1,11 @@ -from titanembeds.utils import socketio, guild_accepts_visitors, get_client_ipaddr -from titanembeds.userbookkeeping import check_user_in_guild, get_guild_channels, update_user_status -from titanembeds.database import db, GuildMembers, get_guild_member +from titanembeds.utils import socketio, guild_accepts_visitors, get_client_ipaddr, discord_api +from titanembeds.userbookkeeping import check_user_in_guild, get_guild_channels, update_user_status, guild_webhooks_enabled +from titanembeds.database import db, GuildMembers, get_guild_member, Guilds from flask_socketio import Namespace, emit, disconnect, join_room, leave_room import functools from flask import request, session import time +import json class Gateway(Namespace): def on_connect(self): @@ -48,6 +49,17 @@ class Gateway(Namespace): else: msg = {"unauthenticated": False, "id": session["user_id"]} emit("embed_user_disconnect", msg, room="GUILD_"+guild_id) + if guild_webhooks_enabled(guild_id): # Delete webhooks + dbguild = db.session.query(Guilds).filter(Guilds.guild_id == guild_id).first() + guild_webhooks = json.loads(dbguild.webhooks) + name = "[Titan] " + if session["unauthenticated"]: + name = name + session["username"] + "#" + str(session["user_id"]) + else: + name = name + session["username"] + "#" + str(session["discriminator"]) + for webhook in guild_webhooks: + if webhook["name"] == name: + discord_api.delete_webhook(webhook["id"], webhook["token"]) def on_heartbeat(self, data): guild_id = data["guild_id"] diff --git a/webapp/titanembeds/blueprints/user/user.py b/webapp/titanembeds/blueprints/user/user.py index 4024a22..9a2b216 100644 --- a/webapp/titanembeds/blueprints/user/user.py +++ b/webapp/titanembeds/blueprints/user/user.py @@ -181,6 +181,7 @@ def administrate_guild(guild_id): "name": db_guild.name, "unauth_users": db_guild.unauth_users, "visitor_view": db_guild.visitor_view, + "webhook_messages": db_guild.webhook_messages, "chat_links": db_guild.chat_links, "bracket_links": db_guild.bracket_links, "mentions_limit": db_guild.mentions_limit, @@ -199,6 +200,7 @@ def update_administrate_guild(guild_id): abort(400) db_guild.unauth_users = request.form.get("unauth_users", db_guild.unauth_users) in ["true", True] db_guild.visitor_view = request.form.get("visitor_view", db_guild.visitor_view) in ["true", True] + db_guild.webhook_messages = request.form.get("webhook_messages", db_guild.webhook_messages) in ["true", True] db_guild.chat_links = request.form.get("chat_links", db_guild.chat_links) in ["true", True] db_guild.bracket_links = request.form.get("bracket_links", db_guild.bracket_links) in ["true", True] db_guild.mentions_limit = request.form.get("mentions_limit", db_guild.mentions_limit) @@ -212,6 +214,8 @@ def update_administrate_guild(guild_id): id=db_guild.id, guild_id=db_guild.guild_id, unauth_users=db_guild.unauth_users, + visitor_view=db_guild.visitor_view, + webhook_messages=db_guild.webhook_messages, chat_links=db_guild.chat_links, bracket_links=db_guild.bracket_links, mentions_limit=db_guild.mentions_limit, diff --git a/webapp/titanembeds/database/guilds.py b/webapp/titanembeds/database/guilds.py index 10ff3bd..72db66a 100644 --- a/webapp/titanembeds/database/guilds.py +++ b/webapp/titanembeds/database/guilds.py @@ -7,6 +7,7 @@ class Guilds(db.Model): name = db.Column(db.String(255), nullable=False) # Name unauth_users = db.Column(db.Boolean(), nullable=False, default=1) # If allowed unauth users visitor_view = db.Column(db.Boolean(), nullable=False, default=0) # If users are automatically "signed in" and can view chat + webhook_messages = db.Column(db.Boolean(), nullable=False, default=0) # Use webhooks to send messages instead of the bot chat_links = db.Column(db.Boolean(), nullable=False, default=1) # If users can post links bracket_links = db.Column(db.Boolean(), nullable=False, default=1) # If appending brackets to links to prevent embed mentions_limit = db.Column(db.Integer, nullable=False, default=11) # If there is a limit on the number of mentions in a msg @@ -23,6 +24,7 @@ class Guilds(db.Model): self.name = name self.unauth_users = True # defaults to true self.visitor_view = False + self.webhook_messages = False self.chat_links = True self.bracket_links = True self.mentions_limit = -1 # -1 = unlimited mentions diff --git a/webapp/titanembeds/discordrest.py b/webapp/titanembeds/discordrest.py index 3afc03b..c3490af 100644 --- a/webapp/titanembeds/discordrest.py +++ b/webapp/titanembeds/discordrest.py @@ -145,6 +145,16 @@ class DiscordREST: # Webhook ##################### + def create_webhook(self, channel_id, name, avatar=None): + _endpoint = "/channels/{channel_id}/webhooks".format(channel_id=channel_id) + payload = { + "name": name, + } + if avatar: + payload["avatar"] = avatar + r = self.request("POST", _endpoint, data=payload, json=True) + return r + def execute_webhook(self, webhook_id, webhook_token, username, avatar, content, wait=True): _endpoint = "/webhooks/{id}/{token}".format(id=webhook_id, token=webhook_token) if wait: @@ -155,4 +165,9 @@ class DiscordREST: 'username': username } r = self.request("POST", _endpoint, data=payload) + return r + + def delete_webhook(self, webhook_id, webhook_token): + _endpoint = "/webhooks/{id}/{token}".format(id=webhook_id, token=webhook_token) + r = self.request("DELETE", _endpoint) return r \ No newline at end of file diff --git a/webapp/titanembeds/static/js/administrate_guild.js b/webapp/titanembeds/static/js/administrate_guild.js index 78c3b1b..c189827 100644 --- a/webapp/titanembeds/static/js/administrate_guild.js +++ b/webapp/titanembeds/static/js/administrate_guild.js @@ -16,6 +16,15 @@ $('#visitor_view').change(function() { }); }); +$('#webhook_messages').change(function() { + var pathname = window.location.pathname; + var checked = $(this).is(':checked') + var payload = {"webhook_messages": checked} + $.post(pathname, payload, function(data) { + Materialize.toast('Updated webhook messages setting!', 2000) + }); +}); + $('#chat_links').change(function() { var pathname = window.location.pathname; var checked = $(this).is(':checked') diff --git a/webapp/titanembeds/templates/administrate_guild.html.j2 b/webapp/titanembeds/templates/administrate_guild.html.j2 index 27578c2..9353c27 100644 --- a/webapp/titanembeds/templates/administrate_guild.html.j2 +++ b/webapp/titanembeds/templates/administrate_guild.html.j2 @@ -62,6 +62,19 @@ +
+ +

Toggle Webhooks Messages

+

Instead of sending user messages directly from the Titan bot, webhook messages allows Titan to take advantage of the built-in webhooks to create messages that looks more real. Reading messages in Discord can be 20% more cooler!

+
+ +
+

Chat Links

diff --git a/webapp/titanembeds/userbookkeeping.py b/webapp/titanembeds/userbookkeeping.py index 576a161..13954af 100644 --- a/webapp/titanembeds/userbookkeeping.py +++ b/webapp/titanembeds/userbookkeeping.py @@ -176,4 +176,8 @@ def get_guild_channels(guild_id, force_everyone=False): result["mention_everyone"] = False result_channels.append(result) - return sorted(result_channels, key=lambda k: k['channel']['position']) \ No newline at end of file + return sorted(result_channels, key=lambda k: k['channel']['position']) + +def guild_webhooks_enabled(guild_id): + dbguild = db.session.query(Guilds).filter(Guilds.guild_id == guild_id).first() + return dbguild.webhook_messages \ No newline at end of file