mirror of
https://github.com/TitanEmbeds/Titan.git
synced 2024-11-14 18:11:23 +01:00
commit
52a24d7618
@ -1,5 +1,5 @@
|
||||
# Titan
|
||||
**Visit our website! [https://titanembeds.tk/](https://titanembeds.tk/) And get started *right away*!**
|
||||
**Visit our website! [https://titanembeds.com/](https://titanembeds.com/) And get started *right away*!**
|
||||
|
||||
There was a time when Discord doesn't support embedding the chat on a webpage. But with Titan, you can! It is as simple as 1, 2, 3!
|
||||
1. Invite the bot to your server (You must have "Manage Server" permissions)
|
||||
@ -14,11 +14,11 @@ There was a time when Discord doesn't support embedding the chat on a webpage. B
|
||||
|
||||
# Installation
|
||||
Would you like to run your own copy of Titan Embeds? There are two parts that integrate nicely together. The webapp (website) handles the frontend and communication with the database to retrieve server messages, etc. The discordbot (bot) handles the communcation
|
||||
between Discord's websockets and pushing out the data to the database for the webapp. Check out the respective folder for their installation (pay attention to the python versions!) instructions.
|
||||
between Discord's websockets and pushing out the data to the database for the webapp. Check out the respective folder for their installation instructions.
|
||||
|
||||
# Database installation
|
||||
To set up the database for it to work with the webapp and the discordbot, one must use **alembic** to *migrate* their databases to the current database state. To do so, please follow these instructions.
|
||||
1. Install alembic with **Python 2.7's pip** `pip install alembic`
|
||||
1. Install alembic with **Python 3.5's pip** `pip install alembic`
|
||||
2. Change your directory to the webapp where the alembic files are located `cd webapp`
|
||||
3. Clone `alembic.example.ini` into your own `alembic.ini` file to find and edit the following line `sqlalchemy.url` to equal your database uri. [See here](http://docs.sqlalchemy.org/en/latest/core/engines.html#database-urls) if you need help understanding how database uri works in SQLalchemy.
|
||||
4. In your terminal, run `alembic upgrade head` to upgrade your database tables to the current version on git. As long as there are only *INFO* messages and no errors, you should be fine.
|
||||
|
@ -9,17 +9,13 @@ cp ~/workspace/webapp/config.example.py ~/workspace/webapp/config.py
|
||||
cp ~/workspace/discordbot/config.example.py ~/workspace/discordbot/config.py
|
||||
cp ~/workspace/webapp/alembic.example.ini ~/workspace/webapp/alembic.ini
|
||||
|
||||
echo "[C9Setup] Installing discordbot dependencies"
|
||||
cd ~/workspace/discordbot/
|
||||
echo "[C9Setup] Installing Titan dependencies"
|
||||
cd ~/workspace/
|
||||
sudo python3.5 -m pip install -r requirements.txt
|
||||
sudo python3.5 -m pip install pymysql
|
||||
|
||||
echo "[C9Setup] Installing webapp dependencies"
|
||||
cd ~/workspace/webapp
|
||||
sudo pip install -r requirements.txt
|
||||
sudo pip install alembic pymysql
|
||||
sudo python3.5 -m pip install alembic pymysql eventlet uwsgi
|
||||
|
||||
echo "[C9Setup] Auto populating alembic.ini database url and titan database table"
|
||||
cd ~/workspace/webapp
|
||||
#sqlalchemy.url = mysql+pymysql://root@localhost/titan
|
||||
sed -i '32s/.*/sqlalchemy.url = mysql+pymysql:\/\/root@localhost\/titan/' ~/workspace/webapp/alembic.ini
|
||||
alembic upgrade head
|
||||
@ -34,6 +30,7 @@ sed -i "11s/.*/\'database-uri\': \"mysql+pymysql:\/\/root@localhost\/titan\",/"
|
||||
sed -i "8s/.*/\'app-location\': \"\/home\/ubuntu\/workspace\/webapp\/\",/" ~/workspace/webapp/config.py
|
||||
|
||||
echo "[C9Setup] Making sure everything can be ran"
|
||||
cd ~/workspace/
|
||||
sudo chmod -R 777 *
|
||||
|
||||
echo "------------------------------"
|
||||
|
@ -3,5 +3,7 @@ config = {
|
||||
|
||||
'database-uri': "driver://username:password@host:port/database",
|
||||
|
||||
'redis-uri': "redis://",
|
||||
|
||||
'errorreporting-channelid': "",
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
https://github.com/TitanEmbeds/discord.py/archive/async.zip#egg=discord.py[voice]
|
||||
sqlalchemy
|
||||
asyncio_extras
|
@ -1,6 +1,7 @@
|
||||
from config import config
|
||||
from titanembeds.database import DatabaseInterface
|
||||
from titanembeds.commands import Commands
|
||||
from titanembeds.socketio import SocketIOInterface
|
||||
import discord
|
||||
import aiohttp
|
||||
import asyncio
|
||||
@ -17,6 +18,7 @@ class Titan(discord.Client):
|
||||
self.http.user_agent += ' TitanEmbeds-Bot'
|
||||
self.database = DatabaseInterface(self)
|
||||
self.command = Commands(self, self.database)
|
||||
self.socketio = SocketIOInterface(self, config["redis-uri"])
|
||||
|
||||
self.database_connected = False
|
||||
self.loop.create_task(self.send_webserver_heartbeat())
|
||||
@ -78,7 +80,7 @@ class Titan(discord.Client):
|
||||
print('------')
|
||||
|
||||
await self.change_presence(
|
||||
game=discord.Game(name="Embed your Discord server! Visit https://TitanEmbeds.tk/"), status=discord.Status.online
|
||||
game=discord.Game(name="Embed your Discord server! Visit https://TitanEmbeds.com/"), status=discord.Status.online
|
||||
)
|
||||
|
||||
try:
|
||||
@ -123,6 +125,7 @@ class Titan(discord.Client):
|
||||
return
|
||||
await self.wait_until_dbonline()
|
||||
await self.database.push_message(message)
|
||||
await self.socketio.on_message(message)
|
||||
|
||||
msg_arr = message.content.split() # split the message
|
||||
if len(message.content.split()) > 1 and message.server: #making sure there is actually stuff in the message and have arguments and check if it is sent in server (not PM)
|
||||
@ -136,10 +139,12 @@ class Titan(discord.Client):
|
||||
async def on_message_edit(self, message_before, message_after):
|
||||
await self.wait_until_dbonline()
|
||||
await self.database.update_message(message_after)
|
||||
await self.socketio.on_message_update(message_after)
|
||||
|
||||
async def on_message_delete(self, message):
|
||||
await self.wait_until_dbonline()
|
||||
await self.database.delete_message(message)
|
||||
await self.socketio.on_message_delete(message)
|
||||
|
||||
async def on_server_join(self, guild):
|
||||
await self.wait_until_dbonline()
|
||||
@ -166,46 +171,56 @@ class Titan(discord.Client):
|
||||
async def on_server_update(self, guildbefore, guildafter):
|
||||
await self.wait_until_dbonline()
|
||||
await self.database.update_guild(guildafter)
|
||||
await self.socketio.on_guild_update(guildafter)
|
||||
|
||||
async def on_server_role_create(self, role):
|
||||
await self.wait_until_dbonline()
|
||||
if role.name == self.user.name and role.managed:
|
||||
await asyncio.sleep(2)
|
||||
await self.database.update_guild(role.server)
|
||||
await self.socketio.on_guild_role_create(role)
|
||||
|
||||
async def on_server_role_delete(self, role):
|
||||
await self.wait_until_dbonline()
|
||||
if role.server.me not in role.server.members:
|
||||
return
|
||||
await self.database.update_guild(role.server)
|
||||
await self.socketio.on_guild_role_delete(role)
|
||||
|
||||
async def on_server_role_update(self, rolebefore, roleafter):
|
||||
await self.wait_until_dbonline()
|
||||
await self.database.update_guild(roleafter.server)
|
||||
await self.socketio.on_guild_role_update(role)
|
||||
|
||||
async def on_channel_delete(self, channel):
|
||||
await self.wait_until_dbonline()
|
||||
await self.database.update_guild(channel.server)
|
||||
await self.socketio.on_channel_delete(channel)
|
||||
|
||||
async def on_channel_create(self, channel):
|
||||
await self.wait_until_dbonline()
|
||||
await self.database.update_guild(channel.server)
|
||||
await self.socketio.on_channel_create(channel)
|
||||
|
||||
async def on_channel_update(self, channelbefore, channelafter):
|
||||
await self.wait_until_dbonline()
|
||||
await self.database.update_guild(channelafter.server)
|
||||
await self.socketio.on_channel_update(channelafter)
|
||||
|
||||
async def on_member_join(self, member):
|
||||
await self.wait_until_dbonline()
|
||||
await self.database.update_guild_member(member, active=True, banned=False)
|
||||
await self.socketio.on_guild_member_add(member)
|
||||
|
||||
async def on_member_remove(self, member):
|
||||
await self.wait_until_dbonline()
|
||||
await self.database.update_guild_member(member, active=False, banned=False)
|
||||
await self.socketio.on_guild_member_remove(member)
|
||||
|
||||
async def on_member_update(self, memberbefore, memberafter):
|
||||
await self.wait_until_dbonline()
|
||||
await self.database.update_guild_member(memberafter)
|
||||
await self.socketio.on_guild_member_update(memberafter)
|
||||
|
||||
async def on_member_ban(self, member):
|
||||
await self.wait_until_dbonline()
|
||||
@ -221,8 +236,10 @@ class Titan(discord.Client):
|
||||
await self.wait_until_dbonline()
|
||||
if len(after) == 0:
|
||||
await self.database.update_guild(before[0].server)
|
||||
await self.socketio.on_guild_emojis_update(before)
|
||||
else:
|
||||
await self.database.update_guild(after[0].server)
|
||||
await self.socketio.on_guild_emojis_update(after)
|
||||
|
||||
async def on_webhooks_update(self, server):
|
||||
await self.wait_until_dbonline()
|
||||
|
@ -18,6 +18,8 @@ from titanembeds.database.unauthenticated_users import UnauthenticatedUsers
|
||||
from titanembeds.database.unauthenticated_bans import UnauthenticatedBans
|
||||
from titanembeds.database.keyvalue_properties import KeyValueProperties
|
||||
|
||||
from titanembeds.utils import get_message_author, get_message_mentions, get_webhooks_list, get_emojis_list, get_roles_list, get_channels_list, list_role_ids
|
||||
|
||||
class DatabaseInterface(object):
|
||||
# Courtesy of https://github.com/SunDwarf/Jokusoramame
|
||||
def __init__(self, bot):
|
||||
@ -58,38 +60,15 @@ class DatabaseInterface(object):
|
||||
message.channel.id,
|
||||
message.id,
|
||||
message.content,
|
||||
json.dumps(self.get_message_author(message)),
|
||||
json.dumps(get_message_author(message)),
|
||||
str(message.timestamp),
|
||||
edit_ts,
|
||||
json.dumps(self.get_message_mentions(message.mentions)),
|
||||
json.dumps(get_message_mentions(message.mentions)),
|
||||
json.dumps(message.attachments)
|
||||
)
|
||||
session.add(msg)
|
||||
session.commit()
|
||||
|
||||
def get_message_author(self, message):
|
||||
author = message.author
|
||||
obj = {
|
||||
"username": author.name,
|
||||
"discriminator": author.discriminator,
|
||||
"bot": author.bot,
|
||||
"id": author.id,
|
||||
"avatar": author.avatar
|
||||
}
|
||||
return obj
|
||||
|
||||
def get_message_mentions(self, mentions):
|
||||
ments = []
|
||||
for author in mentions:
|
||||
ments.append({
|
||||
"username": author.name,
|
||||
"discriminator": author.discriminator,
|
||||
"bot": author.bot,
|
||||
"id": author.id,
|
||||
"avatar": author.avatar
|
||||
})
|
||||
return ments
|
||||
|
||||
async def update_message(self, message):
|
||||
if message.server:
|
||||
async with threadpool():
|
||||
@ -101,9 +80,9 @@ class DatabaseInterface(object):
|
||||
if msg:
|
||||
msg.content = message.content
|
||||
msg.edited_timestamp = message.edited_timestamp
|
||||
msg.mentions = json.dumps(self.get_message_mentions(message.mentions))
|
||||
msg.mentions = json.dumps(get_message_mentions(message.mentions))
|
||||
msg.attachments = json.dumps(message.attachments)
|
||||
msg.author = json.dumps(self.get_message_author(message))
|
||||
msg.author = json.dumps(get_message_author(message))
|
||||
session.commit()
|
||||
|
||||
async def delete_message(self, message):
|
||||
@ -127,91 +106,23 @@ class DatabaseInterface(object):
|
||||
gui = Guilds(
|
||||
guild.id,
|
||||
guild.name,
|
||||
json.dumps(self.get_roles_list(guild.roles)),
|
||||
json.dumps(self.get_channels_list(guild.channels)),
|
||||
json.dumps(self.get_webhooks_list(server_webhooks)),
|
||||
json.dumps(self.get_emojis_list(guild.emojis)),
|
||||
json.dumps(get_roles_list(guild.roles)),
|
||||
json.dumps(get_channels_list(guild.channels)),
|
||||
json.dumps(get_webhooks_list(server_webhooks)),
|
||||
json.dumps(get_emojis_list(guild.emojis)),
|
||||
guild.owner_id,
|
||||
guild.icon
|
||||
)
|
||||
session.add(gui)
|
||||
else:
|
||||
gui.name = guild.name
|
||||
gui.roles = json.dumps(self.get_roles_list(guild.roles))
|
||||
gui.channels = json.dumps(self.get_channels_list(guild.channels))
|
||||
gui.webhooks = json.dumps(self.get_webhooks_list(server_webhooks))
|
||||
gui.emojis = json.dumps(self.get_emojis_list(guild.emojis))
|
||||
gui.roles = json.dumps(get_roles_list(guild.roles))
|
||||
gui.channels = json.dumps(get_channels_list(guild.channels))
|
||||
gui.webhooks = json.dumps(get_webhooks_list(server_webhooks))
|
||||
gui.emojis = json.dumps(get_emojis_list(guild.emojis))
|
||||
gui.owner_id = guild.owner_id
|
||||
gui.icon = guild.icon
|
||||
session.commit()
|
||||
|
||||
def get_webhooks_list(self, guild_webhooks):
|
||||
webhooks = []
|
||||
for webhook in guild_webhooks:
|
||||
webhooks.append({
|
||||
"id": webhook.id,
|
||||
"guild_id": webhook.server.id,
|
||||
"channel_id": webhook.channel.id,
|
||||
"name": webhook.name,
|
||||
"token": webhook.token,
|
||||
})
|
||||
return webhooks
|
||||
|
||||
def get_emojis_list(self, guildemojis):
|
||||
emojis = []
|
||||
for emote in guildemojis:
|
||||
emojis.append({
|
||||
"id": emote.id,
|
||||
"name": emote.name,
|
||||
"require_colons": emote.require_colons,
|
||||
"managed": emote.managed,
|
||||
"roles": self.list_role_ids(emote.roles),
|
||||
"url": emote.url
|
||||
})
|
||||
return emojis
|
||||
|
||||
def get_roles_list(self, guildroles):
|
||||
roles = []
|
||||
for role in guildroles:
|
||||
roles.append({
|
||||
"id": role.id,
|
||||
"name": role.name,
|
||||
"color": role.color.value,
|
||||
"hoist": role.hoist,
|
||||
"position": role.position,
|
||||
"permissions": role.permissions.value
|
||||
})
|
||||
return roles
|
||||
|
||||
def get_channels_list(self, guildchannels):
|
||||
channels = []
|
||||
for channel in guildchannels:
|
||||
if str(channel.type) == "text":
|
||||
overwrites = []
|
||||
for target, overwrite in channel.overwrites:
|
||||
if isinstance(target, discord.Role):
|
||||
type = "role"
|
||||
else:
|
||||
type = "member"
|
||||
allow, deny = overwrite.pair()
|
||||
allow = allow.value
|
||||
deny = deny.value
|
||||
overwrites.append({
|
||||
"id": target.id,
|
||||
"type": type,
|
||||
"allow": allow,
|
||||
"deny": deny,
|
||||
})
|
||||
|
||||
channels.append({
|
||||
"id": channel.id,
|
||||
"name": channel.name,
|
||||
"topic": channel.topic,
|
||||
"position": channel.position,
|
||||
"type": str(channel.type),
|
||||
"permission_overwrites": overwrites
|
||||
})
|
||||
return channels
|
||||
|
||||
async def remove_unused_guilds(self, guilds):
|
||||
async with threadpool():
|
||||
@ -256,7 +167,7 @@ class DatabaseInterface(object):
|
||||
member.avatar,
|
||||
active,
|
||||
banned,
|
||||
json.dumps(self.list_role_ids(member.roles))
|
||||
json.dumps(list_role_ids(member.roles))
|
||||
)
|
||||
session.add(dbmember)
|
||||
else:
|
||||
@ -266,7 +177,7 @@ class DatabaseInterface(object):
|
||||
dbmember.discriminator = member.discriminator
|
||||
dbmember.nickname = member.nick
|
||||
dbmember.avatar = member.avatar
|
||||
dbmember.roles = json.dumps(self.list_role_ids(member.roles))
|
||||
dbmember.roles = json.dumps(list_role_ids(member.roles))
|
||||
session.commit()
|
||||
|
||||
async def unban_server_user(self, user, server):
|
||||
@ -294,12 +205,6 @@ class DatabaseInterface(object):
|
||||
if changed:
|
||||
session.commit()
|
||||
|
||||
def list_role_ids(self, usr_roles):
|
||||
ids = []
|
||||
for role in usr_roles:
|
||||
ids.append(role.id)
|
||||
return ids
|
||||
|
||||
async def flag_unactive_bans(self, guild_id, guildbans):
|
||||
async with threadpool():
|
||||
with self.get_session() as session:
|
||||
|
@ -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
|
||||
|
1
discordbot/titanembeds/socketio/__init__.py
Normal file
1
discordbot/titanembeds/socketio/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from .socketiointerface import SocketIOInterface
|
174
discordbot/titanembeds/socketio/socketiointerface.py
Normal file
174
discordbot/titanembeds/socketio/socketiointerface.py
Normal file
@ -0,0 +1,174 @@
|
||||
import socketio
|
||||
from titanembeds.utils import get_message_author, get_message_mentions, get_roles_list
|
||||
import time
|
||||
from email import utils as emailutils
|
||||
|
||||
class SocketIOInterface:
|
||||
def __init__(self, bot, redis_uri):
|
||||
self.io = socketio.AsyncRedisManager(redis_uri, write_only=True, channel='flask-socketio')
|
||||
self.bot = bot
|
||||
|
||||
def format_datetime(self, datetimeobj):
|
||||
return emailutils.formatdate(time.mktime(datetimeobj.timetuple())) # https://stackoverflow.com/questions/3453177/convert-python-datetime-to-rfc-2822
|
||||
|
||||
def get_formatted_message(self, message):
|
||||
edit_ts = message.edited_timestamp
|
||||
if not edit_ts:
|
||||
edit_ts = None
|
||||
else:
|
||||
edit_ts = self.format_datetime(edit_ts)
|
||||
msg = {
|
||||
"id": message.id,
|
||||
"channel_id": message.channel.id,
|
||||
"content": message.content,
|
||||
"author": get_message_author(message),
|
||||
"timestamp": self.format_datetime(message.timestamp),
|
||||
"edited_timestamp": edit_ts,
|
||||
"mentions": get_message_mentions(message.mentions),
|
||||
"attachments": message.attachments,
|
||||
}
|
||||
nickname = None
|
||||
if hasattr(message.author, 'nick') and message.author.nick:
|
||||
nickname = message.author.nick
|
||||
msg["author"]["nickname"] = nickname
|
||||
for mention in msg["mentions"]:
|
||||
mention["nickname"] = None
|
||||
member = message.server.get_member(mention["id"])
|
||||
if member:
|
||||
mention["nickname"] = member.nick
|
||||
return msg
|
||||
|
||||
async def on_message(self, message):
|
||||
if message.server:
|
||||
msg = self.get_formatted_message(message)
|
||||
await self.io.emit('MESSAGE_CREATE', data=msg, room=str("CHANNEL_"+message.channel.id), namespace='/gateway')
|
||||
|
||||
async def on_message_delete(self, message):
|
||||
if message.server:
|
||||
msg = self.get_formatted_message(message)
|
||||
await self.io.emit('MESSAGE_DELETE', data=msg, room=str("CHANNEL_"+message.channel.id), namespace='/gateway')
|
||||
|
||||
async def on_message_update(self, message):
|
||||
if message.server:
|
||||
msg = self.get_formatted_message(message)
|
||||
await self.io.emit('MESSAGE_UPDATE', data=msg, room=str("CHANNEL_"+message.channel.id), namespace='/gateway')
|
||||
|
||||
def get_formatted_user(self, user):
|
||||
userobj = {
|
||||
"avatar": user.avatar,
|
||||
"avatar_url": user.avatar_url,
|
||||
"color": str(user.color)[1:],
|
||||
"discriminator": user.discriminator,
|
||||
"game": None,
|
||||
"hoist-role": None,
|
||||
"id": user.id,
|
||||
"status": str(user.status),
|
||||
"username": user.name,
|
||||
"nick": None,
|
||||
}
|
||||
if userobj["color"] == "000000":
|
||||
userobj["color"] = None
|
||||
# if userobj["avatar_url"][len(userobj["avatar_url"])-15:] != ".jpg":
|
||||
# userobj["avatar_url"] = userobj["avatar_url"][:len(userobj["avatar_url"])-14] + ".jpg"
|
||||
if user.nick:
|
||||
userobj["nick"] = user.nick
|
||||
if user.game:
|
||||
userobj["game"] = {
|
||||
"name": user.game.name
|
||||
}
|
||||
roles = sorted(user.roles, key=lambda k: k.position, reverse=True)
|
||||
for role in roles:
|
||||
if role.hoist:
|
||||
userobj["hoist-role"] = {
|
||||
"id": role.id,
|
||||
"name": role.name,
|
||||
"position": role.position,
|
||||
}
|
||||
break
|
||||
return userobj
|
||||
|
||||
async def on_guild_member_add(self, member):
|
||||
user = self.get_formatted_user(member)
|
||||
await self.io.emit('GUILD_MEMBER_ADD', data=user, room=str("GUILD_"+member.server.id), namespace='/gateway')
|
||||
|
||||
async def on_guild_member_remove(self, member):
|
||||
user = self.get_formatted_user(member)
|
||||
await self.io.emit('GUILD_MEMBER_REMOVE', data=user, room=str("GUILD_"+member.server.id), namespace='/gateway')
|
||||
|
||||
async def on_guild_member_update(self, member):
|
||||
user = self.get_formatted_user(member)
|
||||
await self.io.emit('GUILD_MEMBER_UPDATE', data=user, room=str("GUILD_"+member.server.id), namespace='/gateway')
|
||||
|
||||
def get_formatted_emojis(self, emojis):
|
||||
emotes = []
|
||||
for emo in emojis:
|
||||
emotes.append({
|
||||
"id": emo.id,
|
||||
"managed": emo.managed,
|
||||
"name": emo.name,
|
||||
"require_colons": emo.require_colons,
|
||||
"roles": get_roles_list(emo.roles),
|
||||
"url": emo.url,
|
||||
})
|
||||
return emotes
|
||||
|
||||
async def on_guild_emojis_update(self, emojis):
|
||||
emotes = self.get_formatted_emojis(emojis)
|
||||
await self.io.emit('GUILD_EMOJIS_UPDATE', data=emotes, room=str("GUILD_"+emojis[0].server.id), namespace='/gateway')
|
||||
|
||||
def get_formatted_guild(self, guild):
|
||||
guil = {
|
||||
"id": guild.id,
|
||||
"name": guild.name,
|
||||
"icon": guild.icon,
|
||||
"icon_url": guild.icon_url,
|
||||
}
|
||||
return guil
|
||||
|
||||
async def on_guild_update(self, guild):
|
||||
guildobj = self.get_formatted_guild(guild)
|
||||
await self.io.emit('GUILD_UPDATE', data=guildobj, room=str("GUILD_"+guild.id), namespace='/gateway')
|
||||
|
||||
def get_formatted_channel(self, channel):
|
||||
chan = {
|
||||
"id": channel.id,
|
||||
"guild_id": channel.server.id,
|
||||
}
|
||||
return chan
|
||||
|
||||
async def on_channel_delete(self, channel):
|
||||
if str(channel.type) != "text":
|
||||
return
|
||||
chan = self.get_formatted_channel(channel)
|
||||
await self.io.emit('CHANNEL_DELETE', data=chan, room=str("GUILD_"+channel.server.id), namespace='/gateway')
|
||||
|
||||
async def on_channel_create(self, channel):
|
||||
if str(channel.type) != "text":
|
||||
return
|
||||
chan = self.get_formatted_channel(channel)
|
||||
await self.io.emit('CHANNEL_CREATE', data=chan, room=str("GUILD_"+channel.server.id), namespace='/gateway')
|
||||
|
||||
async def on_channel_update(self, channel):
|
||||
if str(channel.type) != "text":
|
||||
return
|
||||
chan = self.get_formatted_channel(channel)
|
||||
await self.io.emit('CHANNEL_UPDATE', data=chan, room=str("GUILD_"+channel.server.id), namespace='/gateway')
|
||||
|
||||
def get_formatted_role(self, role):
|
||||
rol = {
|
||||
"id": role.id,
|
||||
"guild_id": role.server.id,
|
||||
}
|
||||
return rol
|
||||
|
||||
async def on_guild_role_create(self, role):
|
||||
rol = self.get_formatted_role(role)
|
||||
await self.io.emit('GUILD_ROLE_CREATE', data=rol, room=str("GUILD_"+role.server.id), namespace='/gateway')
|
||||
|
||||
async def on_guild_role_update(self, role):
|
||||
rol = self.get_formatted_role(role)
|
||||
await self.io.emit('GUILD_ROLE_UPDATE', data=rol, room=str("GUILD_"+role.server.id), namespace='/gateway')
|
||||
|
||||
async def on_guild_role_delete(self, role):
|
||||
rol = self.get_formatted_role(role)
|
||||
await self.io.emit('GUILD_ROLE_DELETE', data=rol, room=str("GUILD_"+role.server.id), namespace='/gateway')
|
98
discordbot/titanembeds/utils.py
Normal file
98
discordbot/titanembeds/utils.py
Normal file
@ -0,0 +1,98 @@
|
||||
import discord
|
||||
|
||||
def get_message_author(message):
|
||||
author = message.author
|
||||
obj = {
|
||||
"username": author.name,
|
||||
"discriminator": author.discriminator,
|
||||
"bot": author.bot,
|
||||
"id": author.id,
|
||||
"avatar": author.avatar
|
||||
}
|
||||
return obj
|
||||
|
||||
def get_message_mentions(mentions):
|
||||
ments = []
|
||||
for author in mentions:
|
||||
ments.append({
|
||||
"username": author.name,
|
||||
"discriminator": author.discriminator,
|
||||
"bot": author.bot,
|
||||
"id": author.id,
|
||||
"avatar": author.avatar
|
||||
})
|
||||
return ments
|
||||
|
||||
def get_webhooks_list(guild_webhooks):
|
||||
webhooks = []
|
||||
for webhook in guild_webhooks:
|
||||
webhooks.append({
|
||||
"id": webhook.id,
|
||||
"guild_id": webhook.server.id,
|
||||
"channel_id": webhook.channel.id,
|
||||
"name": webhook.name,
|
||||
"token": webhook.token,
|
||||
})
|
||||
return webhooks
|
||||
|
||||
def get_emojis_list(guildemojis):
|
||||
emojis = []
|
||||
for emote in guildemojis:
|
||||
emojis.append({
|
||||
"id": emote.id,
|
||||
"name": emote.name,
|
||||
"require_colons": emote.require_colons,
|
||||
"managed": emote.managed,
|
||||
"roles": list_role_ids(emote.roles),
|
||||
"url": emote.url
|
||||
})
|
||||
return emojis
|
||||
|
||||
def get_roles_list(guildroles):
|
||||
roles = []
|
||||
for role in guildroles:
|
||||
roles.append({
|
||||
"id": role.id,
|
||||
"name": role.name,
|
||||
"color": role.color.value,
|
||||
"hoist": role.hoist,
|
||||
"position": role.position,
|
||||
"permissions": role.permissions.value
|
||||
})
|
||||
return roles
|
||||
|
||||
def get_channels_list(guildchannels):
|
||||
channels = []
|
||||
for channel in guildchannels:
|
||||
if str(channel.type) == "text":
|
||||
overwrites = []
|
||||
for target, overwrite in channel.overwrites:
|
||||
if isinstance(target, discord.Role):
|
||||
type = "role"
|
||||
else:
|
||||
type = "member"
|
||||
allow, deny = overwrite.pair()
|
||||
allow = allow.value
|
||||
deny = deny.value
|
||||
overwrites.append({
|
||||
"id": target.id,
|
||||
"type": type,
|
||||
"allow": allow,
|
||||
"deny": deny,
|
||||
})
|
||||
|
||||
channels.append({
|
||||
"id": channel.id,
|
||||
"name": channel.name,
|
||||
"topic": channel.topic,
|
||||
"position": channel.position,
|
||||
"type": str(channel.type),
|
||||
"permission_overwrites": overwrites
|
||||
})
|
||||
return channels
|
||||
|
||||
def list_role_ids(usr_roles):
|
||||
ids = []
|
||||
for role in usr_roles:
|
||||
ids.append(role.id)
|
||||
return ids
|
12
requirements.txt
Normal file
12
requirements.txt
Normal file
@ -0,0 +1,12 @@
|
||||
flask
|
||||
flask-sqlalchemy
|
||||
flask_limiter
|
||||
requests_oauthlib
|
||||
Flask-SSLify
|
||||
flask_socketio
|
||||
paypalrestsdk
|
||||
https://github.com/TitanEmbeds/discord.py/archive/async.zip#egg=discord.py[voice]
|
||||
asyncio_extras
|
||||
kombu
|
||||
redis
|
||||
aioredis
|
@ -2,7 +2,7 @@
|
||||
The webapp portion handles the frontend (it's what the users see). The webapp highly depends on the discordbot to push websockets data to the database.
|
||||
|
||||
# Installation
|
||||
1. Clone the repo (make sure you have **Python 2.7** installed on your system. This webapp portion depends on that specific python version)
|
||||
1. Clone the repo (make sure you have **Python 3.5** installed on your system. This webapp portion depends on that specific python version)
|
||||
2. Install the pip requirements `pip install -r requirements.txt`
|
||||
3. Clone `config.example.py` and rename it to `config.py`. Edit the file to your standards
|
||||
4. Run the development web via `python run.py` -- Though we suggest to use a better server software (look into gunicorn, nginx, uwsgi, etc)
|
@ -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 ###
|
@ -13,4 +13,6 @@ config = {
|
||||
'app-secret': "Type something random here, go wild.",
|
||||
|
||||
'database-uri': "driver://username:password@host:port/database",
|
||||
'redis-uri': "redis://",
|
||||
'websockets-mode': "LITTERALLY None or eventlet or gevent",
|
||||
}
|
||||
|
@ -1,6 +0,0 @@
|
||||
flask
|
||||
flask-sqlalchemy
|
||||
flask_limiter
|
||||
requests_oauthlib
|
||||
Flask-SSLify
|
||||
paypalrestsdk
|
@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python2
|
||||
from titanembeds.app import app
|
||||
from titanembeds.app import app, socketio
|
||||
import subprocess
|
||||
|
||||
def init_debug():
|
||||
@ -41,4 +41,4 @@ def init_debug():
|
||||
return "OK"
|
||||
if __name__ == "__main__":
|
||||
init_debug()
|
||||
app.run(host="0.0.0.0",port=3000,debug=True,processes=3)
|
||||
socketio.run(app, host="0.0.0.0",port=3000,debug=True)
|
||||
|
@ -1,6 +1,6 @@
|
||||
from run import app, init_debug
|
||||
from run import app, socketio, init_debug
|
||||
import os
|
||||
|
||||
if __name__ == "__main__":
|
||||
init_debug()
|
||||
app.run(host=os.getenv('IP', '0.0.0.0'), port=int(os.getenv('PORT', 8080)), debug=True, processes=3)
|
||||
socketio.run(app, host=os.getenv('IP', '0.0.0.0'), port=int(os.getenv('PORT', 8080)), debug=True)
|
@ -1,15 +1,23 @@
|
||||
from config import config
|
||||
from database import db
|
||||
from .database import db
|
||||
from flask import Flask, render_template, request, session, url_for, redirect, jsonify
|
||||
from flask_sslify import SSLify
|
||||
from titanembeds.utils import rate_limiter, discord_api, bot_alive
|
||||
import blueprints.api
|
||||
import blueprints.user
|
||||
import blueprints.admin
|
||||
import blueprints.embed
|
||||
from titanembeds.utils import rate_limiter, discord_api, bot_alive, socketio
|
||||
from .blueprints import api, user, admin, embed, gateway
|
||||
import os
|
||||
from titanembeds.database import get_administrators_list
|
||||
|
||||
try:
|
||||
import uwsgi
|
||||
from gevent import monkey
|
||||
monkey.patch_all()
|
||||
except:
|
||||
if config.get("websockets-mode", None) == "eventlet":
|
||||
import eventlet
|
||||
eventlet.monkey_patch()
|
||||
elif config.get("websockets-mode", None) == "gevent":
|
||||
from gevent import monkey
|
||||
monkey.patch_all()
|
||||
|
||||
os.chdir(config['app-location'])
|
||||
app = Flask(__name__, static_folder="static")
|
||||
@ -23,11 +31,13 @@ app.secret_key = config['app-secret']
|
||||
db.init_app(app)
|
||||
rate_limiter.init_app(app)
|
||||
sslify = SSLify(app, permanent=True)
|
||||
socketio.init_app(app, message_queue=config["redis-uri"], path='gateway', async_mode=config.get("websockets-mode", None))
|
||||
|
||||
app.register_blueprint(blueprints.api.api, url_prefix="/api", template_folder="/templates")
|
||||
app.register_blueprint(blueprints.admin.admin, url_prefix="/admin", template_folder="/templates")
|
||||
app.register_blueprint(blueprints.user.user, url_prefix="/user", template_folder="/templates")
|
||||
app.register_blueprint(blueprints.embed.embed, url_prefix="/embed", template_folder="/templates")
|
||||
app.register_blueprint(api.api, url_prefix="/api", template_folder="/templates")
|
||||
app.register_blueprint(admin.admin, url_prefix="/admin", template_folder="/templates")
|
||||
app.register_blueprint(user.user, url_prefix="/user", template_folder="/templates")
|
||||
app.register_blueprint(embed.embed, url_prefix="/embed", template_folder="/templates")
|
||||
socketio.on_namespace(gateway.Gateway('/gateway'))
|
||||
|
||||
@app.route("/")
|
||||
def index():
|
||||
|
@ -1 +1 @@
|
||||
from admin import admin
|
||||
from .admin import admin
|
@ -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,
|
||||
|
@ -1 +1 @@
|
||||
from api import api
|
||||
from .api import api
|
||||
|
@ -2,11 +2,11 @@ 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.database import get_administrators_list
|
||||
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_
|
||||
import random
|
||||
import requests
|
||||
import json
|
||||
import datetime
|
||||
import re
|
||||
@ -14,97 +14,6 @@ from config import config
|
||||
|
||||
api = Blueprint("api", __name__)
|
||||
|
||||
def user_unauthenticated():
|
||||
if 'unauthenticated' in session:
|
||||
return session['unauthenticated']
|
||||
return True
|
||||
|
||||
def checkUserRevoke(guild_id, user_key=None):
|
||||
revoked = True #guilty until proven not revoked
|
||||
if user_unauthenticated():
|
||||
dbUser = UnauthenticatedUsers.query.filter(and_(UnauthenticatedUsers.guild_id == guild_id, UnauthenticatedUsers.user_key == user_key)).first()
|
||||
revoked = dbUser.isRevoked()
|
||||
else:
|
||||
banned = checkUserBanned(guild_id)
|
||||
if banned:
|
||||
return revoked
|
||||
dbUser = GuildMembers.query.filter(GuildMembers.guild_id == guild_id).filter(GuildMembers.user_id == session["user_id"]).first()
|
||||
revoked = not dbUser or not dbUser.active
|
||||
return revoked
|
||||
|
||||
def checkUserBanned(guild_id, ip_address=None):
|
||||
banned = True
|
||||
if user_unauthenticated():
|
||||
dbUser = UnauthenticatedBans.query.filter(and_(UnauthenticatedBans.guild_id == guild_id, UnauthenticatedBans.ip_address == ip_address)).all()
|
||||
if not dbUser:
|
||||
banned = False
|
||||
else:
|
||||
for usr in dbUser:
|
||||
if usr.lifter_id is not None:
|
||||
banned = False
|
||||
else:
|
||||
banned = False
|
||||
dbUser = GuildMembers.query.filter(GuildMembers.guild_id == guild_id).filter(GuildMembers.user_id == session["user_id"]).first()
|
||||
if not dbUser:
|
||||
banned = False
|
||||
else:
|
||||
banned = dbUser.banned
|
||||
return banned
|
||||
|
||||
def update_user_status(guild_id, username, user_key=None):
|
||||
if user_unauthenticated():
|
||||
ip_address = get_client_ipaddr()
|
||||
status = {
|
||||
'authenticated': False,
|
||||
'avatar': None,
|
||||
'manage_embed': False,
|
||||
'ip_address': ip_address,
|
||||
'username': username,
|
||||
'nickname': None,
|
||||
'user_key': user_key,
|
||||
'guild_id': guild_id,
|
||||
'user_id': session['user_id'],
|
||||
'banned': checkUserBanned(guild_id, ip_address),
|
||||
'revoked': checkUserRevoke(guild_id, user_key),
|
||||
}
|
||||
if status['banned'] or status['revoked']:
|
||||
session['user_keys'].pop(guild_id, None)
|
||||
return status
|
||||
dbUser = UnauthenticatedUsers.query.filter(and_(UnauthenticatedUsers.guild_id == guild_id, UnauthenticatedUsers.user_key == user_key)).first()
|
||||
dbUser.bumpTimestamp()
|
||||
if dbUser.username != username or dbUser.ip_address != ip_address:
|
||||
dbUser.username = username
|
||||
dbUser.ip_address = ip_address
|
||||
db.session.commit()
|
||||
else:
|
||||
status = {
|
||||
'authenticated': True,
|
||||
'avatar': session["avatar"],
|
||||
'manage_embed': check_user_can_administrate_guild(guild_id),
|
||||
'username': username,
|
||||
'nickname': None,
|
||||
'discriminator': session['discriminator'],
|
||||
'guild_id': guild_id,
|
||||
'user_id': session['user_id'],
|
||||
'banned': checkUserBanned(guild_id),
|
||||
'revoked': checkUserRevoke(guild_id)
|
||||
}
|
||||
if status['banned'] or status['revoked']:
|
||||
return status
|
||||
dbMember = get_guild_member(guild_id, status["user_id"])
|
||||
if dbMember:
|
||||
status["nickname"] = dbMember.nickname
|
||||
dbUser = db.session.query(AuthenticatedUsers).filter(and_(AuthenticatedUsers.guild_id == guild_id, AuthenticatedUsers.client_id == status['user_id'])).first()
|
||||
dbUser.bumpTimestamp()
|
||||
return status
|
||||
|
||||
def check_user_in_guild(guild_id):
|
||||
if user_unauthenticated():
|
||||
return guild_id in session['user_keys']
|
||||
else:
|
||||
dbUser = db.session.query(AuthenticatedUsers).filter(and_(AuthenticatedUsers.guild_id == guild_id, AuthenticatedUsers.client_id == session['user_id'])).first()
|
||||
return dbUser is not None and not checkUserRevoke(guild_id)
|
||||
|
||||
def parse_emoji(textToParse, guild_id):
|
||||
guild_emojis = get_guild_emojis(guild_id)
|
||||
for gemoji in guild_emojis:
|
||||
@ -141,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:
|
||||
@ -160,92 +69,6 @@ def format_everyone_mention(channel, content):
|
||||
content = content.replace("@here", u"@\u200Bhere")
|
||||
return content
|
||||
|
||||
def get_member_roles(guild_id, user_id):
|
||||
q = db.session.query(GuildMembers).filter(GuildMembers.guild_id == guild_id).filter(GuildMembers.user_id == user_id).first()
|
||||
return json.loads(q.roles)
|
||||
|
||||
def get_dbguild_channels(guild_id):
|
||||
q = db.session.query(Guilds).filter(Guilds.guild_id == guild_id).first()
|
||||
return json.loads(q.channels)
|
||||
|
||||
def get_guild_channels(guild_id, force_everyone=False):
|
||||
if user_unauthenticated() or force_everyone:
|
||||
member_roles = [guild_id] #equivilant to @everyone role
|
||||
else:
|
||||
member_roles = get_member_roles(guild_id, session['user_id'])
|
||||
if guild_id not in member_roles:
|
||||
member_roles.append(guild_id)
|
||||
dbguild = db.session.query(Guilds).filter(Guilds.guild_id == guild_id).first()
|
||||
guild_channels = json.loads(dbguild.channels)
|
||||
guild_roles = json.loads(dbguild.roles)
|
||||
guild_owner = str(dbguild.owner_id)
|
||||
result_channels = []
|
||||
for channel in guild_channels:
|
||||
if channel['type'] == "text":
|
||||
result = {"channel": channel, "read": False, "write": False, "mention_everyone": False}
|
||||
if guild_owner == session.get("user_id"):
|
||||
result["read"] = True
|
||||
result["write"] = True
|
||||
result["mention_everyone"] = True
|
||||
result_channels.append(result)
|
||||
continue
|
||||
channel_perm = 0
|
||||
|
||||
# @everyone
|
||||
for role in guild_roles:
|
||||
if role["id"] == guild_id:
|
||||
channel_perm |= role["permissions"]
|
||||
continue
|
||||
|
||||
# User Guild Roles
|
||||
for m_role in member_roles:
|
||||
for g_role in guild_roles:
|
||||
if g_role["id"] == m_role:
|
||||
channel_perm |= g_role["permissions"]
|
||||
continue
|
||||
|
||||
# If has server administrator permission
|
||||
if user_has_permission(channel_perm, 3):
|
||||
result["read"] = True
|
||||
result["write"] = True
|
||||
result["mention_everyone"] = True
|
||||
result_channels.append(result)
|
||||
continue
|
||||
|
||||
denies = 0
|
||||
allows = 0
|
||||
|
||||
# channel specific
|
||||
for overwrite in channel["permission_overwrites"]:
|
||||
if overwrite["type"] == "role" and overwrite["id"] in member_roles:
|
||||
denies |= overwrite["deny"]
|
||||
allows |= overwrite["allow"]
|
||||
|
||||
channel_perm = (channel_perm & ~denies) | allows
|
||||
|
||||
# member specific
|
||||
for overwrite in channel["permission_overwrites"]:
|
||||
if overwrite["type"] == "member" and overwrite["id"] == session.get("user_id"):
|
||||
channel_perm = (channel_perm & ~overwrite['deny']) | overwrite['allow']
|
||||
break
|
||||
|
||||
result["read"] = user_has_permission(channel_perm, 10)
|
||||
result["write"] = user_has_permission(channel_perm, 11)
|
||||
result["mention_everyone"] = user_has_permission(channel_perm, 17)
|
||||
|
||||
# If default channel, you can read
|
||||
if channel["id"] == guild_id:
|
||||
result["read"] = True
|
||||
|
||||
# If you cant read channel, you cant write in it
|
||||
if not user_has_permission(channel_perm, 10):
|
||||
result["read"] = False
|
||||
result["write"] = False
|
||||
result["mention_everyone"] = False
|
||||
|
||||
result_channels.append(result)
|
||||
return sorted(result_channels, key=lambda k: k['channel']['position'])
|
||||
|
||||
def filter_guild_channel(guild_id, channel_id, force_everyone=False):
|
||||
channels = get_guild_channels(guild_id, force_everyone)
|
||||
for chan in channels:
|
||||
@ -287,7 +110,7 @@ def get_online_discord_users(guild_id, embed):
|
||||
return embed['members']
|
||||
|
||||
def get_online_embed_users(guild_id):
|
||||
time_past = (datetime.datetime.now() - datetime.timedelta(seconds = 60)).strftime('%Y-%m-%d %H:%M:%S')
|
||||
time_past = (datetime.datetime.now() - datetime.timedelta(seconds = 15)).strftime('%Y-%m-%d %H:%M:%S')
|
||||
unauths = db.session.query(UnauthenticatedUsers).filter(UnauthenticatedUsers.last_timestamp > time_past, UnauthenticatedUsers.revoked == False, UnauthenticatedUsers.guild_id == guild_id).all()
|
||||
auths = db.session.query(AuthenticatedUsers).filter(AuthenticatedUsers.last_timestamp > time_past, AuthenticatedUsers.guild_id == guild_id).all()
|
||||
users = {'unauthenticated':[], 'authenticated':[]}
|
||||
@ -316,15 +139,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)
|
||||
@ -343,6 +174,7 @@ def fetch():
|
||||
status_code = 403
|
||||
if user_unauthenticated():
|
||||
session['user_keys'].pop(guild_id, None)
|
||||
session.modified = True
|
||||
else:
|
||||
chan = filter_guild_channel(guild_id, channel_id)
|
||||
if not chan:
|
||||
@ -379,7 +211,7 @@ def fetch_visitor():
|
||||
|
||||
@api.route("/post", methods=["POST"])
|
||||
@valid_session_required(api=True)
|
||||
@rate_limiter.limit("1 per 10 second", key_func = channel_ratelimit_key)
|
||||
@rate_limiter.limit("1 per 5 second", key_func = channel_ratelimit_key)
|
||||
def post():
|
||||
guild_id = request.form.get("guild_id")
|
||||
channel_id = request.form.get('channel_id')
|
||||
@ -487,6 +319,7 @@ def change_unauthenticated_username():
|
||||
if not checkUserBanned(guild_id, ip_address):
|
||||
if 'user_keys' not in session or guild_id not in session['user_keys'] or not session['unauthenticated']:
|
||||
abort(424)
|
||||
emitmsg = {"unauthenticated": True, "username": session["username"], "discriminator": session["user_id"]}
|
||||
session['username'] = username
|
||||
if 'user_id' not in session or len(str(session["user_id"])) > 4:
|
||||
session['user_id'] = random.randint(0,9999)
|
||||
@ -496,6 +329,7 @@ def change_unauthenticated_username():
|
||||
key = user.user_key
|
||||
session['user_keys'][guild_id] = key
|
||||
status = update_user_status(guild_id, username, key)
|
||||
emit("embed_user_disconnect", emitmsg, room="GUILD_"+guild_id, namespace="/gateway")
|
||||
return jsonify(status=status)
|
||||
else:
|
||||
status = {'banned': True}
|
||||
|
@ -1 +1 @@
|
||||
from embed import embed
|
||||
from .embed import embed
|
||||
|
1
webapp/titanembeds/blueprints/gateway/__init__.py
Normal file
1
webapp/titanembeds/blueprints/gateway/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from .gateway import Gateway
|
106
webapp/titanembeds/blueprints/gateway/gateway.py
Normal file
106
webapp/titanembeds/blueprints/gateway/gateway.py
Normal file
@ -0,0 +1,106 @@
|
||||
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):
|
||||
emit('hello')
|
||||
|
||||
def on_identify(self, data):
|
||||
guild_id = data["guild_id"]
|
||||
if not guild_accepts_visitors(guild_id) and not check_user_in_guild(guild_id):
|
||||
disconnect()
|
||||
return
|
||||
session["socket_guild_id"] = guild_id
|
||||
channels = []
|
||||
if guild_accepts_visitors(guild_id) and not check_user_in_guild(guild_id):
|
||||
channels = get_guild_channels(guild_id, force_everyone=True)
|
||||
else:
|
||||
channels = get_guild_channels(guild_id)
|
||||
join_room("GUILD_"+guild_id)
|
||||
for chan in channels:
|
||||
if chan["read"]:
|
||||
join_room("CHANNEL_"+chan["channel"]["id"])
|
||||
if session.get("unauthenticated", True) and guild_id in session.get("user_keys", {}):
|
||||
join_room("IP_"+get_client_ipaddr())
|
||||
elif not session.get("unauthenticated", True):
|
||||
join_room("USER_"+session["user_id"])
|
||||
visitor_mode = data["visitor_mode"]
|
||||
if not visitor_mode:
|
||||
if session["unauthenticated"]:
|
||||
emit("embed_user_connect", {"unauthenticated": True, "username": session["username"], "discriminator": session["user_id"]}, room="GUILD_"+guild_id)
|
||||
else:
|
||||
nickname = db.session.query(GuildMembers).filter(GuildMembers.guild_id == guild_id, GuildMembers.user_id == session["user_id"]).first().nickname
|
||||
emit("embed_user_connect", {"unauthenticated": False, "id": session["user_id"], "nickname": nickname, "username": session["username"],"discriminator": session["discriminator"], "avatar_url": session["avatar"]}, room="GUILD_"+guild_id)
|
||||
emit("identified")
|
||||
|
||||
def on_disconnect(self):
|
||||
if "user_keys" not in session:
|
||||
return
|
||||
guild_id = session["socket_guild_id"]
|
||||
msg = {}
|
||||
if session["unauthenticated"]:
|
||||
msg = {"unauthenticated": True, "username": session["username"], "discriminator": session["user_id"]}
|
||||
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"]
|
||||
visitor_mode = data["visitor_mode"]
|
||||
if not visitor_mode:
|
||||
key = None
|
||||
if session["unauthenticated"]:
|
||||
key = session["user_keys"][guild_id]
|
||||
status = update_user_status(guild_id, session["username"], key)
|
||||
if status["revoked"] or status["banned"]:
|
||||
emit("revoke")
|
||||
time.sleep(1000)
|
||||
disconnect()
|
||||
else:
|
||||
if not guild_accepts_visitors(guild_id):
|
||||
disconnect()
|
||||
|
||||
def on_channel_list(self, data):
|
||||
guild_id = data["guild_id"]
|
||||
visitor_mode = data["visitor_mode"]
|
||||
channels = None
|
||||
if visitor_mode or session.get("unauthenticated", True):
|
||||
channels = get_guild_channels(guild_id, True)
|
||||
else:
|
||||
channels = get_guild_channels(guild_id)
|
||||
for chan in channels:
|
||||
if chan["read"]:
|
||||
join_room("CHANNEL_"+chan["channel"]["id"])
|
||||
else:
|
||||
leave_room("CHANNEL_"+chan["channel"]["id"])
|
||||
emit("channel_list", channels)
|
||||
|
||||
def on_current_user_info(self, data):
|
||||
guild_id = data["guild_id"]
|
||||
if "user_keys" in session and not session["unauthenticated"]:
|
||||
dbMember = get_guild_member(guild_id, session["user_id"])
|
||||
usr = {
|
||||
'avatar': session["avatar"],
|
||||
'username': dbMember.username,
|
||||
'nickname': dbMember.nickname,
|
||||
'discriminator': dbMember.discriminator,
|
||||
'user_id': session['user_id'],
|
||||
}
|
||||
emit("current_user_info", usr)
|
@ -1 +1 @@
|
||||
from user import user
|
||||
from .user import user
|
||||
|
@ -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,
|
||||
|
@ -2,18 +2,18 @@ from flask_sqlalchemy import SQLAlchemy
|
||||
|
||||
db = SQLAlchemy()
|
||||
|
||||
from guilds import Guilds
|
||||
from unauthenticated_users import UnauthenticatedUsers
|
||||
from unauthenticated_bans import UnauthenticatedBans
|
||||
from authenticated_users import AuthenticatedUsers
|
||||
from guild_members import GuildMembers, list_all_guild_members, get_guild_member
|
||||
from keyvalue_properties import KeyValueProperties, set_keyvalproperty, get_keyvalproperty, getexpir_keyvalproperty, setexpir_keyvalproperty, ifexists_keyvalproperty, delete_keyvalproperty
|
||||
from messages import Messages, get_channel_messages
|
||||
from cosmetics import Cosmetics
|
||||
from user_css import UserCSS
|
||||
from administrators import Administrators, get_administrators_list
|
||||
from titan_tokens import TitanTokens, get_titan_token
|
||||
from token_transactions import TokenTransactions
|
||||
from .guilds import Guilds
|
||||
from .unauthenticated_users import UnauthenticatedUsers
|
||||
from .unauthenticated_bans import UnauthenticatedBans
|
||||
from .authenticated_users import AuthenticatedUsers
|
||||
from .guild_members import GuildMembers, list_all_guild_members, get_guild_member
|
||||
from .keyvalue_properties import KeyValueProperties, set_keyvalproperty, get_keyvalproperty, getexpir_keyvalproperty, setexpir_keyvalproperty, ifexists_keyvalproperty, delete_keyvalproperty
|
||||
from .messages import Messages, get_channel_messages
|
||||
from .cosmetics import Cosmetics
|
||||
from .user_css import UserCSS
|
||||
from .administrators import Administrators, get_administrators_list
|
||||
from .titan_tokens import TitanTokens, get_titan_token
|
||||
from .token_transactions import TokenTransactions
|
||||
|
||||
def set_titan_token(user_id, amt_change, action):
|
||||
token_count = get_titan_token(user_id)
|
||||
|
@ -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
|
||||
|
@ -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
|
@ -45,14 +45,14 @@ def user_has_permission(permission, index):
|
||||
return bool((int(permission) >> index) & 1)
|
||||
|
||||
def get_user_guilds():
|
||||
cache = get_keyvalproperty("OAUTH/USERGUILDS/"+make_user_cache_key())
|
||||
cache = get_keyvalproperty("OAUTH/USERGUILDS/"+str(make_user_cache_key()))
|
||||
if cache:
|
||||
return cache
|
||||
req = discordrest_from_user("/users/@me/guilds")
|
||||
if req.status_code != 200:
|
||||
abort(req.status_code)
|
||||
req = json.dumps(req.json())
|
||||
set_keyvalproperty("OAUTH/USERGUILDS/"+make_user_cache_key(), req, 250)
|
||||
set_keyvalproperty("OAUTH/USERGUILDS/"+str(make_user_cache_key()), req, 250)
|
||||
return req
|
||||
|
||||
def get_user_managed_servers():
|
||||
|
BIN
webapp/titanembeds/static/img/people/appledash.png
Normal file
BIN
webapp/titanembeds/static/img/people/appledash.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 535 KiB |
BIN
webapp/titanembeds/static/img/people/dotjs.jpg
Normal file
BIN
webapp/titanembeds/static/img/people/dotjs.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.1 KiB |
Binary file not shown.
Before Width: | Height: | Size: 58 KiB |
@ -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')
|
||||
|
@ -7,25 +7,27 @@
|
||||
/* global localStorage */
|
||||
/* global visitors_enabled */
|
||||
/* global cheet */
|
||||
/* global location */
|
||||
/* global io */
|
||||
|
||||
(function () {
|
||||
const theme_options = ["DiscordDark", "BetterTitan"]; // All the avaliable theming names
|
||||
|
||||
var user_def_css; // Saves the user defined css
|
||||
var has_already_been_focused = false; // keep track of if the embed has initially been focused.
|
||||
var has_already_been_initially_resized = false; // keep track if the embed initially been resized
|
||||
var logintimer; // timer to keep track of user inactivity after hitting login
|
||||
var fetchtimeout; // fetch routine timer
|
||||
var currently_fetching; // fetch lock- if true, do not fetch
|
||||
var last_message_id; // last message tracked
|
||||
var selected_channel = null; // user selected channel
|
||||
var guild_channels = {}; // all server channels used to highlight channels in messages
|
||||
var emoji_store = {}; // all server emojis
|
||||
var times_fetched = 0; // kept track of how many times that it has fetched
|
||||
var fetch_error_count = 0; // Number of errors fetch has encountered
|
||||
var priority_query_guild = false; // So you have selected a channel? Let's populate it.
|
||||
var emoji_store = []; // all server emojis
|
||||
var current_username_discrim; // Current username/discrim pair, eg EndenDraogn#4151
|
||||
var current_user_discord_id; // Current user discord snowflake id, eg mine is 140252024666062848
|
||||
var visitor_mode = false; // Keep track of if using the visitor mode or authenticate mode
|
||||
var socket = null; // Socket.io object
|
||||
var authenticated_users_list = []; // List of all authenticated users
|
||||
var unauthenticated_users_list = []; // List of all guest users
|
||||
var discord_users_list = []; // List of all discord users that are probably online
|
||||
var guild_channels_list = []; // guild channels, but as a list of them
|
||||
var shift_pressed = false; // Track down if shift pressed on messagebox
|
||||
|
||||
function element_in_view(element, fullyInView) {
|
||||
@ -137,16 +139,6 @@
|
||||
}
|
||||
);
|
||||
$('#loginmodal').modal('open');
|
||||
|
||||
$("#focusmodal").modal({
|
||||
dismissible: true,
|
||||
opacity: .5,
|
||||
inDuration: 400,
|
||||
outDuration: 400,
|
||||
startingTop: "4%",
|
||||
endingTop: "10%",
|
||||
});
|
||||
$("#focusmodal").modal("open");
|
||||
$("#userembedmodal").modal({
|
||||
dismissible: true,
|
||||
opacity: .5,
|
||||
@ -235,23 +227,11 @@
|
||||
}
|
||||
});
|
||||
|
||||
primeEmbed();
|
||||
setInterval(send_socket_heartbeat, 5000);
|
||||
if (getParameterByName("username")) {
|
||||
$("#custom_username_field").val(getParameterByName("username"));
|
||||
}
|
||||
|
||||
if (getParameterByName("forcefocus") == "1") {
|
||||
if (document.hasFocus()) {
|
||||
primeEmbed();
|
||||
}
|
||||
|
||||
$(window).focus(function() {
|
||||
if (!has_already_been_focused) {
|
||||
primeEmbed();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
primeEmbed();
|
||||
}
|
||||
});
|
||||
|
||||
function changeTheme(theme=null, keep_custom_css=true) {
|
||||
@ -300,9 +280,6 @@
|
||||
}
|
||||
|
||||
function primeEmbed() {
|
||||
$("#focusmodal").modal("close");
|
||||
has_already_been_focused = true;
|
||||
|
||||
lock_login_fields();
|
||||
|
||||
var guild = query_guild();
|
||||
@ -342,6 +319,10 @@
|
||||
}
|
||||
|
||||
function initialize_embed(guildobj) {
|
||||
if (socket) {
|
||||
socket.disconnect();
|
||||
socket = null;
|
||||
}
|
||||
if (guildobj === undefined) {
|
||||
var guild = query_guild();
|
||||
guild.done(function(data) {
|
||||
@ -382,9 +363,11 @@
|
||||
fill_unauthenticated_users(guildobj.embedmembers.unauthenticated);
|
||||
$("#instant-inv").attr("href", guildobj.instant_invite);
|
||||
run_fetch_routine();
|
||||
initiate_websockets();
|
||||
}
|
||||
|
||||
function fill_channels(channels) {
|
||||
guild_channels_list = channels;
|
||||
var template = $('#mustache_channellistings').html();
|
||||
Mustache.parse(template);
|
||||
$("#channels-list").empty();
|
||||
@ -438,6 +421,7 @@
|
||||
}
|
||||
|
||||
function fill_discord_members(discordmembers) {
|
||||
discord_users_list = discordmembers;
|
||||
var template = $('#mustache_authedusers').html();
|
||||
Mustache.parse(template);
|
||||
$("#discord-members").empty();
|
||||
@ -483,6 +467,21 @@
|
||||
var rendered_role = Mustache.render(template_role, {"name": roleobj["name"] + " - " + roleobj["members"].length});
|
||||
discordmembercnt += roleobj["members"].length;
|
||||
$("#discord-members").append(rendered_role);
|
||||
roleobj.members.sort(function(a, b){
|
||||
var name_a = a.username;
|
||||
var name_b = b.username;
|
||||
if (a.nick) {
|
||||
name_a = a.nick;
|
||||
}
|
||||
if (b.nick) {
|
||||
name_b = b.nick;
|
||||
}
|
||||
name_a = name_a.toUpperCase();
|
||||
name_b = name_b.toUpperCase();
|
||||
if(name_a < name_b) return -1;
|
||||
if(name_a > name_b) return 1;
|
||||
return 0;
|
||||
});
|
||||
for (var j = 0; j < roleobj.members.length; j++) {
|
||||
var member = roleobj.members[j];
|
||||
var member_name = member.nick;
|
||||
@ -519,6 +518,7 @@
|
||||
mention_member(event.data.member_id);
|
||||
});
|
||||
}
|
||||
authenticated_users_list = users;
|
||||
}
|
||||
|
||||
function fill_unauthenticated_users(users) {
|
||||
@ -531,6 +531,7 @@
|
||||
var rendered = Mustache.render(template, {"username": member.username, "discriminator": member.discriminator});
|
||||
$("#embed-unauth-users").append(rendered);
|
||||
}
|
||||
unauthenticated_users_list = users;
|
||||
}
|
||||
|
||||
function wait_for_discord_login() {
|
||||
@ -562,8 +563,6 @@
|
||||
last_message_id = null;
|
||||
$("#channels-list > li.active").removeClass("active");
|
||||
$("#channel-"+selected_channel).parent().addClass("active");
|
||||
priority_query_guild = true;
|
||||
clearTimeout(fetchtimeout);
|
||||
run_fetch_routine();
|
||||
}
|
||||
}
|
||||
@ -685,7 +684,7 @@
|
||||
return text;
|
||||
}
|
||||
|
||||
function fill_discord_messages(messages, jumpscroll) {
|
||||
function fill_discord_messages(messages, jumpscroll, replace=null) {
|
||||
if (messages.length == 0) {
|
||||
return last_message_id;
|
||||
}
|
||||
@ -707,12 +706,19 @@
|
||||
username = message.author.nickname;
|
||||
}
|
||||
var rendered = Mustache.render(template, {"id": message.id, "full_timestamp": message.formatted_timestamp, "time": message.formatted_time, "username": username, "discriminator": message.author.discriminator, "content": nl2br(message.content)});
|
||||
$("#chatcontent").append(rendered);
|
||||
if (replace == null) {
|
||||
$("#chatcontent").append(rendered);
|
||||
handle_last_message_mention();
|
||||
$("#chatcontent p:last-child").find(".blockcode").find("br").remove(); // Remove excessive breaks in codeblocks
|
||||
} else {
|
||||
replace.html($(rendered).html());
|
||||
replace.find(".blockcode").find("br").remove();
|
||||
}
|
||||
last = message.id;
|
||||
handle_last_message_mention();
|
||||
$("#chatcontent p:last-child").find(".blockcode").find("br").remove(); // Remove excessive breaks in codeblocks
|
||||
}
|
||||
$("html, body").animate({ scrollTop: $(document).height() }, "slow");
|
||||
if (replace == null) {
|
||||
$("html, body").animate({ scrollTop: $(document).height() }, "slow");
|
||||
}
|
||||
$('#chatcontent').linkify({
|
||||
target: "_blank"
|
||||
});
|
||||
@ -721,11 +727,6 @@
|
||||
}
|
||||
|
||||
function run_fetch_routine() {
|
||||
if (currently_fetching) {
|
||||
return;
|
||||
}
|
||||
currently_fetching = true;
|
||||
times_fetched += 1;
|
||||
var channel_id = selected_channel;
|
||||
var fet;
|
||||
var jumpscroll;
|
||||
@ -746,6 +747,7 @@
|
||||
} else {
|
||||
update_embed_userchip(status.authenticated, status.avatar, status.username, status.nickname, status.user_id, status.discriminator);
|
||||
update_change_username_modal(status.authenticated, status.username);
|
||||
current_user_discord_id = status.user_id;
|
||||
}
|
||||
last_message_id = fill_discord_messages(data.messages, jumpscroll);
|
||||
if (!visitor_mode && status.manage_embed) {
|
||||
@ -753,20 +755,14 @@
|
||||
} else {
|
||||
$("#administrate_link").hide();
|
||||
}
|
||||
if (times_fetched % 10 == 0 || priority_query_guild) {
|
||||
var guild = query_guild();
|
||||
guild.done(function(guildobj) {
|
||||
priority_query_guild = false;
|
||||
fill_channels(guildobj.channels);
|
||||
fill_discord_members(guildobj.discordmembers);
|
||||
fill_authenticated_users(guildobj.embedmembers.authenticated);
|
||||
fill_unauthenticated_users(guildobj.embedmembers.unauthenticated);
|
||||
$("#instant-inv").attr("href", guildobj.instant_invite);
|
||||
fetchtimeout = setTimeout(run_fetch_routine, 5000);
|
||||
});
|
||||
} else {
|
||||
fetchtimeout = setTimeout(run_fetch_routine, 5000);
|
||||
}
|
||||
var guild = query_guild();
|
||||
guild.done(function(guildobj) {
|
||||
fill_channels(guildobj.channels);
|
||||
fill_discord_members(guildobj.discordmembers);
|
||||
fill_authenticated_users(guildobj.embedmembers.authenticated);
|
||||
fill_unauthenticated_users(guildobj.embedmembers.unauthenticated);
|
||||
$("#instant-inv").attr("href", guildobj.instant_invite);
|
||||
});
|
||||
});
|
||||
fet.fail(function(data) {
|
||||
if (data.status == 403) {
|
||||
@ -776,25 +772,9 @@
|
||||
$('#loginmodal').modal('open');
|
||||
Materialize.toast('Session expired! You have been logged out.', 10000);
|
||||
}
|
||||
|
||||
if (data.status != 429) {
|
||||
setVisitorMode(true);
|
||||
if (visitor_mode) {
|
||||
fetchtimeout = setTimeout(run_fetch_routine, 5000);
|
||||
}
|
||||
}
|
||||
});
|
||||
fet.catch(function(data) {
|
||||
if (500 <= data.status && data.status < 600) {
|
||||
if (fetch_error_count % 5 == 0) {
|
||||
Materialize.toast('Fetching messages error! EndenDragon probably broke something. Sorry =(', 10000);
|
||||
}
|
||||
fetch_error_count += 1;
|
||||
fetchtimeout = setTimeout(run_fetch_routine, 10000);
|
||||
}
|
||||
setVisitorMode(true);
|
||||
});
|
||||
fet.always(function() {
|
||||
currently_fetching = false;
|
||||
$("#fetching-indicator").fadeOut(800);
|
||||
});
|
||||
}
|
||||
@ -877,9 +857,12 @@
|
||||
var usr = change_unauthenticated_username($(this).val());
|
||||
usr.done(function(data) {
|
||||
Materialize.toast('Username changed successfully!', 10000);
|
||||
priority_query_guild = true;
|
||||
clearTimeout(fetchtimeout);
|
||||
run_fetch_routine();
|
||||
if (socket) {
|
||||
run_fetch_routine();
|
||||
socket.disconnect();
|
||||
socket = null;
|
||||
}
|
||||
initiate_websockets();
|
||||
});
|
||||
usr.fail(function(data) {
|
||||
if (data.status == 429) {
|
||||
@ -916,8 +899,6 @@
|
||||
var funct = post(selected_channel, $(this).val());
|
||||
funct.done(function(data) {
|
||||
$("#messagebox").val("");
|
||||
clearTimeout(fetchtimeout);
|
||||
run_fetch_routine();
|
||||
});
|
||||
funct.fail(function(data) {
|
||||
Materialize.toast('Failed to send message.', 10000);
|
||||
@ -929,7 +910,7 @@
|
||||
});
|
||||
funct.catch(function(data) {
|
||||
if (data.status == 429) {
|
||||
Materialize.toast('You are sending messages too fast! 1 message per 10 seconds', 10000);
|
||||
Materialize.toast('You are sending messages too fast! 1 message per 5 seconds', 10000);
|
||||
}
|
||||
});
|
||||
funct.always(function() {
|
||||
@ -959,4 +940,202 @@
|
||||
// basically copied and pasted of browser ponies bookmarklet
|
||||
(function (srcs,cfg) { var cbcount = 1; var callback = function () { -- cbcount; if (cbcount === 0) { BrowserPonies.setBaseUrl(cfg.baseurl); if (!BrowserPoniesBaseConfig.loaded) { BrowserPonies.loadConfig(BrowserPoniesBaseConfig); BrowserPoniesBaseConfig.loaded = true; } BrowserPonies.loadConfig(cfg); if (!BrowserPonies.running()) BrowserPonies.start(); } }; if (typeof(BrowserPoniesConfig) === "undefined") { window.BrowserPoniesConfig = {}; } if (typeof(BrowserPoniesBaseConfig) === "undefined") { ++ cbcount; BrowserPoniesConfig.onbasecfg = callback; } if (typeof(BrowserPonies) === "undefined") { ++ cbcount; BrowserPoniesConfig.oninit = callback; } var node = (document.body || document.documentElement || document.getElementsByTagName('head')[0]); for (var id in srcs) { if (document.getElementById(id)) continue; if (node) { var s = document.createElement('script'); s.type = 'text/javascript'; s.id = id; s.src = srcs[id]; node.appendChild(s); } else { document.write('\u003cscript type="text/javscript" src="'+ srcs[id]+'" id="'+id+'"\u003e\u003c/script\u003e'); } } callback();})({"browser-ponies-script":"https://panzi.github.io/Browser-Ponies/browserponies.js","browser-ponies-config":"https://panzi.github.io/Browser-Ponies/basecfg.js"},{"baseurl":"https://panzi.github.io/Browser-Ponies/","fadeDuration":500,"volume":1,"fps":25,"speed":3,"audioEnabled":false,"showFps":false,"showLoadProgress":true,"speakProbability":0.1,"spawn":{"applejack":1,"fluttershy":1,"pinkie pie":1,"rainbow dash":1,"rarity":1,"twilight sparkle":1}});
|
||||
});
|
||||
|
||||
function initiate_websockets() {
|
||||
if (socket) {
|
||||
return;
|
||||
}
|
||||
|
||||
socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port + "/gateway", {path: '/gateway', transports: ['websocket']});
|
||||
socket.on('connect', function () {
|
||||
socket.emit('identify', {"guild_id": guild_id, "visitor_mode": visitor_mode});
|
||||
});
|
||||
|
||||
socket.on("disconnect", function () {
|
||||
|
||||
});
|
||||
|
||||
socket.on("revoke", function () {
|
||||
socket.disconnect();
|
||||
socket = null;
|
||||
$('#loginmodal').modal('open');
|
||||
primeEmbed();
|
||||
Materialize.toast('Authentication error! You have been disconnected by the server.', 10000);
|
||||
});
|
||||
|
||||
socket.on("embed_user_connect", function (msg) {
|
||||
if (msg.unauthenticated) {
|
||||
for (var i = 0; i < unauthenticated_users_list.length; i++) {
|
||||
var item = unauthenticated_users_list[i];
|
||||
if (item.username == msg.username && item.discriminator == msg.discriminator) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
unauthenticated_users_list.push(msg);
|
||||
fill_unauthenticated_users(unauthenticated_users_list);
|
||||
} else {
|
||||
for (var i = 0; i < authenticated_users_list.length; i++) {
|
||||
var item = authenticated_users_list[i];
|
||||
if (item.id == msg.id) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
authenticated_users_list.push(msg);
|
||||
fill_authenticated_users(authenticated_users_list);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("embed_user_disconnect", function (msg) {
|
||||
if (msg.unauthenticated) {
|
||||
for (var i = 0; i < unauthenticated_users_list.length; i++) {
|
||||
var item = unauthenticated_users_list[i];
|
||||
if (item.username == msg.username && item.discriminator == msg.discriminator) {
|
||||
unauthenticated_users_list.splice(i, 1);
|
||||
fill_unauthenticated_users(unauthenticated_users_list);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (var i = 0; i < authenticated_users_list.length; i++) {
|
||||
var item = authenticated_users_list[i];
|
||||
if (item.id == msg.id) {
|
||||
authenticated_users_list.splice(i, 1);
|
||||
fill_authenticated_users(authenticated_users_list);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("MESSAGE_CREATE", function (msg) {
|
||||
var thismsgchan = msg.channel_id;
|
||||
if (selected_channel != thismsgchan) {
|
||||
return;
|
||||
}
|
||||
var jumpscroll = element_in_view($('#discordmessage_'+last_message_id), true);
|
||||
last_message_id = fill_discord_messages([msg], jumpscroll);
|
||||
});
|
||||
|
||||
socket.on("MESSAGE_DELETE", function (msg) {
|
||||
var msgchan = msg.channel_id;
|
||||
if (selected_channel != msgchan) {
|
||||
return;
|
||||
}
|
||||
$("#discordmessage_"+msg.id).parent().remove();
|
||||
last_message_id = $("#chatcontent").find("[id^=discordmessage_]").last().attr('id').substring(15);
|
||||
});
|
||||
|
||||
socket.on("MESSAGE_UPDATE", function (msg) {
|
||||
var msgelem = $("#discordmessage_"+msg.id);
|
||||
if (msgelem.length == 0) {
|
||||
return;
|
||||
}
|
||||
var msgelem_parent = msgelem.parent();
|
||||
fill_discord_messages([msg], false, msgelem_parent);
|
||||
});
|
||||
|
||||
socket.on("GUILD_MEMBER_ADD", function (usr) {
|
||||
if (usr.status != "offline") {
|
||||
discord_users_list.push(usr);
|
||||
fill_discord_members(discord_users_list);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("GUILD_MEMBER_UPDATE", function (usr) {
|
||||
if (usr.id == current_user_discord_id) {
|
||||
update_socket_channels();
|
||||
socket.emit("current_user_info", {"guild_id": guild_id});
|
||||
}
|
||||
for (var i = 0; i < discord_users_list.length; i++) {
|
||||
if (usr.id == discord_users_list[i].id) {
|
||||
discord_users_list.splice(i, 1);
|
||||
if (usr.status != "offline") {
|
||||
discord_users_list.push(usr);
|
||||
}
|
||||
fill_discord_members(discord_users_list);
|
||||
return;
|
||||
}
|
||||
}
|
||||
discord_users_list.push(usr);
|
||||
fill_discord_members(discord_users_list);
|
||||
});
|
||||
|
||||
socket.on("GUILD_MEMBER_REMOVE", function (usr) {
|
||||
for (var i = 0; i < discord_users_list.length; i++) {
|
||||
if (usr.id == discord_users_list[i].id) {
|
||||
discord_users_list.splice(i, 1);
|
||||
fill_discord_members(discord_users_list);
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("GUILD_EMOJIS_UPDATE", function (emo) {
|
||||
emoji_store = emo;
|
||||
});
|
||||
|
||||
socket.on("GUILD_UPDATE", function (guil) {
|
||||
$("#guild_name").text(guil.name);
|
||||
if (guil.icon) {
|
||||
$("#guild_icon").attr("src", guil.icon_url);
|
||||
$("#guild_icon").show();
|
||||
} else {
|
||||
$("#guild_icon").hide();
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("CHANNEL_DELETE", function (chan) {
|
||||
for (var i = 0; i < guild_channels_list.length; i++) {
|
||||
var thatchannel = guild_channels_list[i];
|
||||
if (thatchannel.channel.id == chan.id) {
|
||||
guild_channels_list.splice(i, 1);
|
||||
fill_channels(guild_channels_list);
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("CHANNEL_UPDATE", function (chan) {
|
||||
update_socket_channels();
|
||||
});
|
||||
|
||||
socket.on("CHANNEL_CREATE", function (chan) {
|
||||
update_socket_channels();
|
||||
});
|
||||
|
||||
socket.on("GUILD_ROLE_UPDATE", function (chan) {
|
||||
update_socket_channels();
|
||||
});
|
||||
|
||||
socket.on("GUILD_ROLE_DELETE", function (chan) {
|
||||
update_socket_channels();
|
||||
});
|
||||
|
||||
socket.on("channel_list", function (chans) {
|
||||
fill_channels(chans);
|
||||
for (var i = 0; i < chans.length; i++) {
|
||||
var thischan = chans[i];
|
||||
if (thischan.channel.id == selected_channel) {
|
||||
$("#channeltopic").text(thischan.channel.topic);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("current_user_info", function (usr) {
|
||||
update_embed_userchip(true, usr.avatar, usr.username, usr.nickname, usr.userid, usr.discriminator);
|
||||
});
|
||||
}
|
||||
|
||||
function update_socket_channels() {
|
||||
if (!socket) {
|
||||
return;
|
||||
}
|
||||
socket.emit("channel_list", {"guild_id": guild_id, "visitor_mode": visitor_mode});
|
||||
}
|
||||
|
||||
function send_socket_heartbeat() {
|
||||
if (socket) {
|
||||
socket.emit("heartbeat", {"guild_id": guild_id, "visitor_mode": visitor_mode});
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
@ -44,11 +44,9 @@ etc.</p>
|
||||
|
||||
{% include 'card_queryparams.html.j2' %}
|
||||
|
||||
{% include 'card_webhookmsgs.html.j2' %}
|
||||
|
||||
<h3>Chat with us!</h3>
|
||||
<div class="video-container">
|
||||
<iframe src="https://titanembeds.tk/embed/295085744249110529" frameborder="0"></iframe>
|
||||
<iframe src="https://titanembeds.com/embed/295085744249110529" frameborder="0"></iframe>
|
||||
</div>
|
||||
|
||||
<h3>Cool People</h3>
|
||||
@ -88,12 +86,57 @@ etc.</p>
|
||||
<div class="card-panel indigo lighten-5 z-depth-1 no-height-padding">
|
||||
<div class="row valign-wrapper">
|
||||
<div class="col s2">
|
||||
<img src="{{ url_for('static', filename='img/people/riva.png') }}" alt="Riva" class="circle responsive-img">
|
||||
<img src="{{ url_for('static', filename='img/people/appledash.png') }}" alt="AppleDash" class="circle responsive-img">
|
||||
</div>
|
||||
<div class="col s10">
|
||||
<h5 class="black-text card-title">AppleDash</h5>
|
||||
<p class="black-text flow-text">Server Hosting</p>
|
||||
<p class="black-text">From the shadows of Poniverse, AppleDash swiftly jumps in and offered his server to make websockets possible!</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col s12 m6">
|
||||
<div class="card-panel indigo lighten-5 z-depth-1 no-height-padding">
|
||||
<div class="row valign-wrapper">
|
||||
<div class="col s2">
|
||||
<img src="/static/img/people/riva.png" alt="Riva" class="circle responsive-img">
|
||||
</div>
|
||||
<div class="col s10">
|
||||
<h5 class="black-text card-title">Riva</h5>
|
||||
<p class="black-text flow-text">Bot Hosting</p>
|
||||
<p class="black-text">This guy hosts our lovely bot, without him Titan would probably not exist.</p>
|
||||
<p class="black-text flow-text">Previous Bot Hosting</p>
|
||||
<p class="black-text">This guy hosts our lovely bot, without him Titan would probably not been that great (ahem, indefinitely offline bot).</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col s12 m6">
|
||||
<div class="card-panel indigo lighten-5 z-depth-1 no-height-padding">
|
||||
<div class="row valign-wrapper">
|
||||
<div class="col s2">
|
||||
<img src="/static/img/people/dotjs.jpg" alt="dotJS" class="circle responsive-img">
|
||||
</div>
|
||||
<div class="col s10">
|
||||
<h5 class="black-text card-title">dotJS</h5>
|
||||
<p class="black-text flow-text">CSS Architect</p>
|
||||
<p class="black-text">Although been well known for his name of JavaScript, he helped us design the embeds with his CSS skillz.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col s12 m6">
|
||||
<div class="card-panel indigo lighten-5 z-depth-1 no-height-padding">
|
||||
<div class="row valign-wrapper">
|
||||
<div class="col s2">
|
||||
<img src="{{ url_for('static', filename='img/people/semic.png') }}" alt="Semic" class="circle responsive-img">
|
||||
</div>
|
||||
<div class="col s10">
|
||||
<h5 class="black-text card-title">Semic</h5>
|
||||
<p class="black-text flow-text">Logo Designer</p>
|
||||
<p class="black-text">From our friends over at ProCord, Semic made most of our awesome and heroic Titan logo.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -62,6 +62,19 @@
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
<p class="flow-text">Toggle Webhooks Messages</p>
|
||||
<p>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 <a href="{{ url_for('static', filename='img/webhook_comparison.png') }}" target="_blank" title="A quick comparison between having webhook messages enabled vs disabled for a text channel">20% more cooler</a>!</p>
|
||||
<div class="switch">
|
||||
<label>
|
||||
Disable
|
||||
<input type="checkbox" id="webhook_messages" name="webhook_messages" {% if guild['webhook_messages'] %}checked{% endif %} >
|
||||
<span class="lever"></span>
|
||||
Enable
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
<p class="flow-text">Chat Links</p>
|
||||
@ -178,8 +191,6 @@
|
||||
|
||||
{% include 'card_queryparams.html.j2' %}
|
||||
|
||||
{% include 'card_webhookmsgs.html.j2' %}
|
||||
|
||||
{% endblock %}
|
||||
{% block script %}
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/administrate_guild.js') }}"></script>
|
||||
|
@ -5,7 +5,7 @@
|
||||
<div class="row">
|
||||
<div class="col s12 black-text">
|
||||
<p class="flow-text">Use query parameters to customize your individual embeds out of this world!</p>
|
||||
<p>Query parameters are in the format of key-value pairs. They are appended after your embed url such that it would look like so: <br><em>https://titanembeds.tk/embed/1234567890<strong>?css=1&theme=DiscordDark&forcefocus=1</strong></em></p>
|
||||
<p>Query parameters are in the format of key-value pairs. They are appended after your embed url such that it would look like so: <br><em>https://titanembeds.com/embed/1234567890<strong>?css=1&theme=DiscordDark&theme=DiscordDark</strong></em></p>
|
||||
<p>Below is the reference of all the avaliable query parameters that may be used.</p>
|
||||
<ul class="collection">
|
||||
<li class="collection-item">
|
||||
@ -18,18 +18,6 @@
|
||||
Instead of having the top channel as the first channel your users see, you may change it. Enable Discord's Developer mode in the Appearances tab of the User Settings and copy the channel ID. <br>
|
||||
<em>Eg: defaultchannel=1234567890</em>
|
||||
</li>
|
||||
<li class="collection-item">
|
||||
<strong>forcefocus=<integer></strong> <br>
|
||||
Embeds are activated once the frame is loaded. However, if you have more than one embeds on the page, the rate limits might bite your user's toes. Having this setting set means that the embed will not be loaded until the user mouseovers the frame. <br>
|
||||
<hr>
|
||||
<strong>Avaliable Options:</strong>
|
||||
<ul class="browser-default">
|
||||
<li><strong>0</strong> = disabled (default)</li>
|
||||
<li><strong>1</strong> = enabled</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<em>Eg: forcefocus=1</em>
|
||||
</li>
|
||||
<li class="collection-item">
|
||||
<strong>theme=<string></strong> <br>
|
||||
Want your embed to use one of our premade themes? Look no further! <br>
|
||||
|
@ -1,23 +0,0 @@
|
||||
<h3><strong>NEW!</strong> Webhook Messages</h3>
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<div class="card-panel indigo lighten-5 z-depth-1">
|
||||
<div class="row">
|
||||
<div class="col s12 black-text">
|
||||
<p class="flow-text">Reading messages in Discord can be 20% more cooler!</p>
|
||||
<p>A quick comparison between having webhook messages <em>enabled vs disabled</em> for a text channel.</p>
|
||||
<ul class="collection">
|
||||
<li class="collection-item">
|
||||
<img src="{{ url_for('static', filename='img/webhook_comparison.png') }}" alt="Old vs new comparison of having webhook messages enabled/disabled for the channel">
|
||||
</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p>If you want your messages to look more real, just add a webhook to the channel <strong>starting</strong> with a name of <code>Titan</code> or <code>[Titan]</code> (case insensitive).</p>
|
||||
<img src="{{ url_for('static', filename='img/webhook_inuse.png') }}" alt="Webhook configuration" class="responsive-img">
|
||||
<p><em>(Webhook Messages enabled for the #general channel)</em></p>
|
||||
<p>The new message format should automatically take effect for the channel the webhook is set to send from.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -43,10 +43,8 @@
|
||||
<ul id="guild-nav" class="side-nav">
|
||||
<li>
|
||||
<div class="userView">
|
||||
{% if guild['icon'] %}
|
||||
<img class="circle" src="{{ generate_guild_icon( guild['id'], guild['icon'] ) }}">
|
||||
{% endif %}
|
||||
<span class="name">{{ guild['name']|e }}</span>
|
||||
<img id="guild_icon" class="circle" src="{{ generate_guild_icon( guild['id'], guild['icon'] ) }}" {% if not guild['icon'] %}style="display: none;"{% endif %}>
|
||||
<span id="guild_name" class="name">{{ guild['name']|e }}</span>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
@ -117,13 +115,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="focusmodal" class="modal">
|
||||
<div class="modal-content">
|
||||
<h4>This embed is currently unfocused.</h4>
|
||||
<p class="flow-text">Please click this part of the page to initialize the embed.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="userembedmodal" class="modal">
|
||||
<div class="modal-content">
|
||||
{% if unauth_enabled %}
|
||||
@ -197,6 +188,7 @@
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/jQuery-linkify/2.1.4/linkify.min.js" integrity="sha256-/qh8j6L0/OTx+7iY8BAeLirxCDBsu3P15Ci5bo7BJaU=" crossorigin="anonymous"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/jQuery-linkify/2.1.4/linkify-jquery.min.js" integrity="sha256-BlSfVPlZijMLojgte2AtSget879chk1+8Z8bEH/L4Cs=" crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/cheet.js/0.3.3/cheet.min.js" integrity="sha256-FxQrnIC3BX45JRzOyFUlKiM6dY3A/ZakV6w4WpYyfyA=" crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.3/socket.io.js" integrity="sha256-sdmLD9jD1PIzq3KOQPNSGZYxjv76rds79Mnyk5JNp1M=" crossorigin="anonymous"></script>
|
||||
|
||||
{% raw %}
|
||||
<script id="mustache_channellistings" type="text/template">
|
||||
|
@ -4,6 +4,6 @@
|
||||
n=i.scripts[0];a.src='//www.google-analytics.com/analytics.js';
|
||||
n.parentNode.insertBefore(a,n)}(window,document,'ga');
|
||||
|
||||
ga('create', 'UA-97073231-1', 'auto');
|
||||
ga('create', 'UA-105774754-1', 'auto');
|
||||
ga('send', 'pageview');
|
||||
</script>
|
||||
|
@ -18,7 +18,7 @@ will have CSS cosmetic privilages removed, if caught. Please don't, we check the
|
||||
<p><strong>This user defined CSS has a unique ID of <em style="font-size: 130%;">{{ css.id }}</em>.</strong></p>
|
||||
<p>To use this CSS in the embed, you must apped a <code>?css={{ css.id }}</code> to the embed URL.</p>
|
||||
<p>Something like this will work:</p>
|
||||
<input readonly value="https://titanembeds.tk/embed/1234567890987654321?css={{ css.id }}" id="disabled" type="text" onClick="this.setSelectionRange(48, this.value.length)">
|
||||
<input readonly value="https://titanembeds.com/embed/1234567890987654321?css={{ css.id }}" id="disabled" type="text" onClick="this.setSelectionRange(48, this.value.length)">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
183
webapp/titanembeds/userbookkeeping.py
Normal file
183
webapp/titanembeds/userbookkeeping.py
Normal file
@ -0,0 +1,183 @@
|
||||
from titanembeds.database import db, Guilds, UnauthenticatedUsers, UnauthenticatedBans, AuthenticatedUsers, GuildMembers, get_guild_member
|
||||
from titanembeds.utils import guild_accepts_visitors, guild_query_unauth_users_bool, get_client_ipaddr
|
||||
from titanembeds.oauth import check_user_can_administrate_guild, user_has_permission
|
||||
from flask import session
|
||||
from sqlalchemy import and_
|
||||
import json
|
||||
|
||||
def user_unauthenticated():
|
||||
if 'unauthenticated' in session:
|
||||
return session['unauthenticated']
|
||||
return True
|
||||
|
||||
def checkUserRevoke(guild_id, user_key=None):
|
||||
revoked = True #guilty until proven not revoked
|
||||
if user_unauthenticated():
|
||||
dbUser = db.session.query(UnauthenticatedUsers).filter(UnauthenticatedUsers.guild_id == guild_id, UnauthenticatedUsers.user_key == user_key).first()
|
||||
revoked = dbUser.isRevoked()
|
||||
else:
|
||||
banned = checkUserBanned(guild_id)
|
||||
if banned:
|
||||
return revoked
|
||||
dbUser = GuildMembers.query.filter(GuildMembers.guild_id == guild_id).filter(GuildMembers.user_id == session["user_id"]).first()
|
||||
revoked = not dbUser or not dbUser.active
|
||||
return revoked
|
||||
|
||||
def checkUserBanned(guild_id, ip_address=None):
|
||||
banned = True
|
||||
if user_unauthenticated():
|
||||
dbUser = UnauthenticatedBans.query.filter(and_(UnauthenticatedBans.guild_id == guild_id, UnauthenticatedBans.ip_address == ip_address)).all()
|
||||
if not dbUser:
|
||||
banned = False
|
||||
else:
|
||||
for usr in dbUser:
|
||||
if usr.lifter_id is not None:
|
||||
banned = False
|
||||
else:
|
||||
banned = False
|
||||
dbUser = GuildMembers.query.filter(GuildMembers.guild_id == guild_id).filter(GuildMembers.user_id == session["user_id"]).first()
|
||||
if not dbUser:
|
||||
banned = False
|
||||
else:
|
||||
banned = dbUser.banned
|
||||
return banned
|
||||
|
||||
def update_user_status(guild_id, username, user_key=None):
|
||||
if user_unauthenticated():
|
||||
ip_address = get_client_ipaddr()
|
||||
status = {
|
||||
'authenticated': False,
|
||||
'avatar': None,
|
||||
'manage_embed': False,
|
||||
'ip_address': ip_address,
|
||||
'username': username,
|
||||
'nickname': None,
|
||||
'user_key': user_key,
|
||||
'guild_id': guild_id,
|
||||
'user_id': session['user_id'],
|
||||
'banned': checkUserBanned(guild_id, ip_address),
|
||||
'revoked': checkUserRevoke(guild_id, user_key),
|
||||
}
|
||||
if status['banned'] or status['revoked']:
|
||||
session['user_keys'].pop(guild_id, None)
|
||||
return status
|
||||
dbUser = UnauthenticatedUsers.query.filter(and_(UnauthenticatedUsers.guild_id == guild_id, UnauthenticatedUsers.user_key == user_key)).first()
|
||||
dbUser.bumpTimestamp()
|
||||
if dbUser.username != username or dbUser.ip_address != ip_address:
|
||||
dbUser.username = username
|
||||
dbUser.ip_address = ip_address
|
||||
db.session.commit()
|
||||
else:
|
||||
status = {
|
||||
'authenticated': True,
|
||||
'avatar': session["avatar"],
|
||||
'manage_embed': check_user_can_administrate_guild(guild_id),
|
||||
'username': username,
|
||||
'nickname': None,
|
||||
'discriminator': session['discriminator'],
|
||||
'guild_id': guild_id,
|
||||
'user_id': session['user_id'],
|
||||
'banned': checkUserBanned(guild_id),
|
||||
'revoked': checkUserRevoke(guild_id)
|
||||
}
|
||||
if status['banned'] or status['revoked']:
|
||||
return status
|
||||
dbMember = get_guild_member(guild_id, status["user_id"])
|
||||
if dbMember:
|
||||
status["nickname"] = dbMember.nickname
|
||||
dbUser = db.session.query(AuthenticatedUsers).filter(and_(AuthenticatedUsers.guild_id == guild_id, AuthenticatedUsers.client_id == status['user_id'])).first()
|
||||
dbUser.bumpTimestamp()
|
||||
return status
|
||||
|
||||
def check_user_in_guild(guild_id):
|
||||
if user_unauthenticated():
|
||||
return guild_id in session.get("user_keys", {})
|
||||
else:
|
||||
dbUser = db.session.query(AuthenticatedUsers).filter(and_(AuthenticatedUsers.guild_id == guild_id, AuthenticatedUsers.client_id == session['user_id'])).first()
|
||||
return dbUser is not None and not checkUserRevoke(guild_id)
|
||||
|
||||
def get_member_roles(guild_id, user_id):
|
||||
q = db.session.query(GuildMembers).filter(GuildMembers.guild_id == guild_id).filter(GuildMembers.user_id == user_id).first()
|
||||
return json.loads(q.roles)
|
||||
|
||||
def get_guild_channels(guild_id, force_everyone=False):
|
||||
if user_unauthenticated() or force_everyone:
|
||||
member_roles = [guild_id] #equivilant to @everyone role
|
||||
else:
|
||||
member_roles = get_member_roles(guild_id, session['user_id'])
|
||||
if guild_id not in member_roles:
|
||||
member_roles.append(guild_id)
|
||||
dbguild = db.session.query(Guilds).filter(Guilds.guild_id == guild_id).first()
|
||||
guild_channels = json.loads(dbguild.channels)
|
||||
guild_roles = json.loads(dbguild.roles)
|
||||
guild_owner = str(dbguild.owner_id)
|
||||
result_channels = []
|
||||
for channel in guild_channels:
|
||||
if channel['type'] == "text":
|
||||
result = {"channel": channel, "read": False, "write": False, "mention_everyone": False}
|
||||
if guild_owner == session.get("user_id"):
|
||||
result["read"] = True
|
||||
result["write"] = True
|
||||
result["mention_everyone"] = True
|
||||
result_channels.append(result)
|
||||
continue
|
||||
channel_perm = 0
|
||||
|
||||
# @everyone
|
||||
for role in guild_roles:
|
||||
if role["id"] == guild_id:
|
||||
channel_perm |= role["permissions"]
|
||||
continue
|
||||
|
||||
# User Guild Roles
|
||||
for m_role in member_roles:
|
||||
for g_role in guild_roles:
|
||||
if g_role["id"] == m_role:
|
||||
channel_perm |= g_role["permissions"]
|
||||
continue
|
||||
|
||||
# If has server administrator permission
|
||||
if user_has_permission(channel_perm, 3):
|
||||
result["read"] = True
|
||||
result["write"] = True
|
||||
result["mention_everyone"] = True
|
||||
result_channels.append(result)
|
||||
continue
|
||||
|
||||
denies = 0
|
||||
allows = 0
|
||||
|
||||
# channel specific
|
||||
for overwrite in channel["permission_overwrites"]:
|
||||
if overwrite["type"] == "role" and overwrite["id"] in member_roles:
|
||||
denies |= overwrite["deny"]
|
||||
allows |= overwrite["allow"]
|
||||
|
||||
channel_perm = (channel_perm & ~denies) | allows
|
||||
|
||||
# member specific
|
||||
for overwrite in channel["permission_overwrites"]:
|
||||
if overwrite["type"] == "member" and overwrite["id"] == session.get("user_id"):
|
||||
channel_perm = (channel_perm & ~overwrite['deny']) | overwrite['allow']
|
||||
break
|
||||
|
||||
result["read"] = user_has_permission(channel_perm, 10)
|
||||
result["write"] = user_has_permission(channel_perm, 11)
|
||||
result["mention_everyone"] = user_has_permission(channel_perm, 17)
|
||||
|
||||
# If default channel, you can read
|
||||
if channel["id"] == guild_id:
|
||||
result["read"] = True
|
||||
|
||||
# If you cant read channel, you cant write in it
|
||||
if not user_has_permission(channel_perm, 10):
|
||||
result["read"] = False
|
||||
result["write"] = False
|
||||
result["mention_everyone"] = False
|
||||
|
||||
result_channels.append(result)
|
||||
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
|
@ -1,6 +1,7 @@
|
||||
from titanembeds.database import db, Guilds, KeyValueProperties, get_keyvalproperty
|
||||
from flask import request, session
|
||||
from flask_limiter import Limiter
|
||||
from flask_socketio import SocketIO
|
||||
from config import config
|
||||
import random
|
||||
import string
|
||||
@ -16,12 +17,12 @@ def get_client_ipaddr():
|
||||
ip = request.headers['X-Real-IP']
|
||||
else: # general
|
||||
ip = request.remote_addr
|
||||
return hashlib.sha512(config['app-secret'] + ip).hexdigest()[:15]
|
||||
return hashlib.sha512((config['app-secret'] + ip).encode('utf-8')).hexdigest()[:15]
|
||||
|
||||
def generate_session_key():
|
||||
sess = session.get("sessionunique", None)
|
||||
if not sess:
|
||||
rand_str = lambda n: ''.join([random.choice(string.lowercase) for i in xrange(n)])
|
||||
rand_str = lambda n: ''.join([random.choice(string.ascii_lowercase) for i in range(n)])
|
||||
session['sessionunique'] = rand_str(25)
|
||||
sess = session['sessionunique']
|
||||
return sess #Totally unique
|
||||
@ -31,33 +32,33 @@ def make_cache_key(*args, **kwargs):
|
||||
args = str(hash(frozenset(request.args.items())))
|
||||
ip = get_client_ipaddr()
|
||||
sess = generate_session_key()
|
||||
return (path + args + sess + ip).encode('utf-8')
|
||||
return (path + args + sess + ip)
|
||||
|
||||
def make_user_cache_key(*args, **kwargs):
|
||||
ip = get_client_ipaddr()
|
||||
sess = generate_session_key()
|
||||
return (sess + ip).encode('utf-8')
|
||||
return (sess + ip)
|
||||
|
||||
def make_guilds_cache_key():
|
||||
sess = generate_session_key()
|
||||
ip = get_client_ipaddr()
|
||||
return (sess + ip + "user_guilds").encode('utf-8')
|
||||
return (sess + ip + "user_guilds")
|
||||
|
||||
def make_guildchannels_cache_key():
|
||||
guild_id = request.values.get('guild_id', "0")
|
||||
sess = generate_session_key()
|
||||
ip = get_client_ipaddr()
|
||||
return (sess + ip + guild_id + "user_guild_channels").encode('utf-8')
|
||||
return (sess + ip + guild_id + "user_guild_channels")
|
||||
|
||||
def channel_ratelimit_key(): # Generate a bucket with given channel & unique session key
|
||||
sess = generate_session_key()
|
||||
channel_id = request.values.get('channel_id', "0")
|
||||
return (sess + channel_id).encode('utf-8')
|
||||
return (sess + channel_id)
|
||||
|
||||
def guild_ratelimit_key():
|
||||
ip = get_client_ipaddr()
|
||||
guild_id = request.values.get('guild_id', "0")
|
||||
return (ip + guild_id).encode('utf-8')
|
||||
return (ip + guild_id)
|
||||
|
||||
def check_guild_existance(guild_id):
|
||||
dbGuild = Guilds.query.filter_by(guild_id=guild_id).first()
|
||||
@ -89,3 +90,4 @@ def bot_alive():
|
||||
return results
|
||||
|
||||
rate_limiter = Limiter(key_func=get_client_ipaddr) # Default limit by ip address
|
||||
socketio = SocketIO()
|
Loading…
Reference in New Issue
Block a user