Basic utils for models, added some stuff to the bot

This commit is contained in:
Dustin Pianalto 2019-09-19 21:52:16 -08:00
parent fb9744a93a
commit b3eefa1266
46 changed files with 1271 additions and 149 deletions

View File

@ -78,6 +78,7 @@ class Geeksbot(commands.Bot):
self.config_dir = 'geeksbot/config/'
self.config_file = 'bot_config.json'
self.extension_dir = 'exts'
self.api_token = os.environ['API_TOKEN']
self.aio_session = aiohttp.ClientSession(loop=self.loop)
with open(f'{self.config_dir}{self.config_file}') as f:
self.bot_config = json.load(f)
@ -90,6 +91,16 @@ class Geeksbot(commands.Bot):
self.git_url = 'https://github.com/dustinpianalto/geeksbot_v2'
self.load_default_extensions()
self.book_emojis = {
'unlock': '🔓',
'start': '',
'back': '',
'hash': '#\N{COMBINING ENCLOSING KEYCAP}',
'forward': '',
'end': '',
'close': '🇽',
}
async def load_ext(self, mod):
self.load_extension(f'geeksbot.{self.extension_dir}.{mod}')
logger.info(f'Extension Loaded: {mod}')

View File

@ -64,9 +64,9 @@ class Exec(commands.Cog):
ret = await func()
except Exception:
pag.add(stdout.getvalue())
pag.add(traceback.format_exc())
for page in pag.pages():
await ctx.send(page)
pag.add(f'\n\uFFF8{traceback.format_exc()}')
book = Book(pag, (None, ctx.channel, ctx.bot, ctx.message))
await book.create_book()
else:
value = stdout.getvalue()
# noinspection PyBroadException
@ -76,10 +76,10 @@ class Exec(commands.Cog):
pass
value = format_output(value)
pag.add(value)
pag.add(f'\nReturned: {ret}')
pag.add(f'\n\uFFF8Returned: {ret}')
self._last_result = ret
for page in pag.pages():
await ctx.send(page)
book = Book(pag, (None, ctx.channel, ctx.bot, ctx.message))
await book.create_book()
@commands.command(hidden=True)
async def repl(self, ctx):
@ -161,8 +161,8 @@ class Exec(commands.Cog):
body = self.cleanup_code(body)
pag = Paginator(self.bot)
pag.add(await asyncio.wait_for(self.bot.loop.create_task(run_command(body)), 120))
for page in pag.pages():
await ctx.send(page)
book = Book(pag, (None, ctx.channel, ctx.bot, ctx.message))
await book.create_book()
await ctx.message.add_reaction('')
except asyncio.TimeoutError:
await ctx.send(f"Command did not complete in the time allowed.")

View File

@ -156,7 +156,7 @@ class Paginator:
if not self._embed:
for part in [str(p) for p in self._parts]:
if part == self._page_break:
if part.startswith(self._page_break):
close_page()
new_chars = len(_page) + len(part)
@ -185,9 +185,8 @@ class Paginator:
open_field('\uFFF0')
for part in [str(p) for p in self._parts]:
if part.strip() == self._page_break:
if part.strip().startswith(self._page_break):
close_page()
continue
elif part == self._field_break:
if len(_fields) + 1 < 25:
close_field(next_name='\uFFF0')

View File

View File

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

View File

@ -0,0 +1,5 @@
from django.apps import AppConfig
class ChannelsConfig(AppConfig):
name = 'channels'

View File

@ -0,0 +1,46 @@
from django.db import models
from django.core.exceptions import ObjectDoesNotExist
from rest_framework import status
from geeksbot_v2.guilds.models import Guild
from .utils import create_error_response
from .utils import create_success_response
# Create your models here.
class Channel(models.Model):
id = models.CharField(max_length=30, primary_key=True)
guild = models.ForeignKey(Guild, on_delete=models.CASCADE)
@classmethod
def add_new_channel(cls, data):
id = data.get('id')
if id and cls.get_channel_by_id(id):
return create_error_response('Channel Already Exists',
status=status.HTTP_409_CONFLICT)
guild_id = data.get('guild')
if not (id and guild_id):
return create_error_response('Id and Guild are required',
status=status.HTTP_400_BAD_REQUEST)
guild = Guild.get_guild_by_id(guild_id)
if not isinstance(guild, Guild):
return create_error_response('Guild Does Not Exist',
status=status.HTTP_404_NOT_FOUND)
channel = cls(
id=id,
guild=guild
)
channel.save()
return create_success_response(channel, status.HTTP_201_CREATED, many=False)
@classmethod
def get_channel_by_id(cls, id):
try:
return cls.objects.get(id=id)
except ObjectDoesNotExist:
return None
def __str__(self):
return str(id)

View File

@ -0,0 +1,9 @@
from rest_framework import serializers
from .models import Channel
class ChannelSerializer(serializers.ModelSerializer):
class Meta:
model = Channel
fields = "__all__"

View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

View File

@ -0,0 +1,14 @@
from rest_framework.response import Response
from rest_framework import status
def create_error_response(msg, status=status.HTTP_404_NOT_FOUND):
return Response({'details': msg},
status=status)
def create_success_response(channel_data, status, many: bool = False):
from .serializers import ChannelSerializer
return Response(ChannelSerializer(channel_data, many=many).data,
status=status)

View File

@ -0,0 +1,3 @@
from django.shortcuts import render
# Create your views here.

View File

@ -43,6 +43,7 @@ LOCALE_PATHS = [ROOT_DIR.path("locale")]
# https://docs.djangoproject.com/en/dev/ref/settings/#databases
DATABASES = {"default": env.db("DATABASE_URL")}
DATABASES["default"]["ATOMIC_REQUESTS"] = True
DATABASES['default']['CONN_MAX_AGE'] = 0
# URLS
# ------------------------------------------------------------------------------
@ -263,6 +264,9 @@ ACCOUNT_EMAIL_VERIFICATION = "optional"
ACCOUNT_ADAPTER = "geeksbot_v2.users.adapters.AccountAdapter"
# https://django-allauth.readthedocs.io/en/latest/configuration.html
SOCIALACCOUNT_ADAPTER = "geeksbot_v2.users.adapters.SocialAccountAdapter"
ACCOUNT_FORMS = {
'signup': 'geeksbot_v2.users.forms.UserCreateForm',
}
# Your stuff...
@ -275,6 +279,9 @@ REST_FRAMEWORK = {
"DEFAULT_RENDERER_CLASSES": [
"rest_framework.renderers.JSONRenderer",
],
"DEFAULT_PARSER_CLASSES": [
'rest_framework.parsers.JSONParser',
],
"DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.LimitOffsetPagination",
"PAGE_SIZE": 100,
}

View File

@ -39,4 +39,4 @@ class Migration(migrations.Migration):
bases=(models.Model,),
managers=[("objects", django.contrib.sites.models.SiteManager())],
)
]
]

View File

@ -17,4 +17,4 @@ class Migration(migrations.Migration):
verbose_name="domain name",
),
)
]
]

View File

@ -1,6 +1,5 @@
"""
To understand why this file is here, please read:
http://cookiecutter-django.readthedocs.io/en/latest/faq.html#why-is-there-a-django-contrib-sites-directory-in-cookiecutter-django
"""
from django.conf import settings
@ -31,4 +30,4 @@ class Migration(migrations.Migration):
dependencies = [("sites", "0002_alter_domain_unique")]
operations = [migrations.RunPython(update_site_forward, update_site_backward)]
operations = [migrations.RunPython(update_site_forward, update_site_backward)]

View File

@ -1,4 +1,4 @@
# Generated by Django 2.2.4 on 2019-09-16 05:23
# Generated by Django 2.2.4 on 2019-09-17 19:31
from django.conf import settings
import django.contrib.postgres.fields
@ -11,8 +11,8 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('guilds', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [

View File

@ -1,8 +1,18 @@
from datetime import datetime
from django.db import models
from django.core.exceptions import ObjectDoesNotExist
from django.contrib.postgres.fields import ArrayField
from rest_framework import status
from geeksbot_v2.guilds.models import Guild
from geeksbot_v2.guilds.models import Role
from geeksbot_v2.users.models import User
from geeksbot_v2.channels.models import Channel
from .utils import create_error_response
from .utils import create_success_response
from .utils import create_request_success_response
from .utils import create_comment_success_response
# Create your models here.
@ -11,18 +21,124 @@ class Message(models.Model):
id = models.CharField(max_length=30, primary_key=True)
author = models.ForeignKey(User, on_delete=models.CASCADE)
guild = models.ForeignKey(Guild, on_delete=models.CASCADE)
channel = models.CharField(max_length=30)
channel = models.ForeignKey(Channel, on_delete=models.CASCADE)
created_at = models.DateTimeField()
modified_at = models.DateTimeField(null=True)
deleted_at = models.DateTimeField(null=True)
modified_at = models.DateTimeField(null=True, blank=True)
deleted_at = models.DateTimeField(null=True, blank=True)
content = models.CharField(max_length=2000)
previous_content = ArrayField(models.CharField(max_length=2000))
tagged_users = ArrayField(models.CharField(max_length=30))
tagged_channels = ArrayField(models.CharField(max_length=30))
tagged_roles = ArrayField(models.CharField(max_length=30))
previous_content = ArrayField(models.CharField(max_length=2000), default=[])
tagged_users = models.ManyToManyField(User)
tagged_channels = models.ManyToManyField(Channel)
tagged_roles = models.ManyToManyField(Role)
tagged_everyone = models.BooleanField()
embeds = ArrayField(models.TextField())
previous_embeds = ArrayField(ArrayField(models.TextField()))
embeds = ArrayField(models.TextField(), default=[])
previous_embeds = ArrayField(ArrayField(models.TextField()), default=[])
@classmethod
def add_new_message(cls, data):
id = data.get('id')
if id and cls.get_message_by_id(id):
return create_error_response("Message Already Exists",
status=status.HTTP_409_CONFLICT)
author_id = data.get('author')
guild_id = data.get('guild')
channel_id = data.get('channel')
created_at = data.get('created_at')
content = data.get('content')
tagged_everyone = data.get('tagged_everyone')
if not (id and author_id and guild_id and channel_id and created_at and content and tagged_everyone):
return create_error_response("One or more required fields are missing.",
status=status.HTTP_400_BAD_REQUEST)
author = User.get_user_by_id(author_id)
if not isinstance(author, User):
return create_error_response("Author Does Not Exist",
status=status.HTTP_404_NOT_FOUND)
guild = Guild.get_guild_by_id(guild_id)
if not isinstance(guild, Guild):
return create_error_response("Guild Does Not Exist",
status=status.HTTP_404_NOT_FOUND)
channel = Channel.get_channel_by_id(channel_id)
if not isinstance(channel, Channel):
return create_error_response("Channel Does Not Exist",
status=status.HTTP_404_NOT_FOUND)
created_at = datetime.fromtimestamp(created_at)
message = cls(
id=id,
author=author,
guild=guild,
channel=channel,
created_at=created_at,
tagged_everyone=tagged_everyone or False,
content=content or '',
embeds=data.get('embeds') or []
)
message.save()
if data.get('tagged_users'):
tagged_users = data.get('tagged_users')
for user_id in tagged_users:
user = User.get_user_by_id(user_id)
if user:
message.tagged_users.add(user)
if data.get('tagged_roles'):
tagged_roles = data.get('tagged_roles')
for role_id in tagged_roles:
role = Role.get_role_by_id(role_id)
if role:
message.tagged_roles.add(role)
if data.get('tagged_channels'):
tagged_channels = data.get('tagged_channels')
for channel_id in tagged_channels:
channel = Channel.get_channel_by_id(channel_id)
if channel:
message.tagged_channels.add(channel)
return create_success_response(message, status.HTTP_201_CREATED, many=False)
def update_message(self, data):
if data.get('modified_at'):
self.modified_at = data.get('modified_at')
if data.get('deleted_at'):
self.modified_at = data.get('deleted_at')
if data.get('content'):
content = data.get('content')
if content != self.content:
self.previous_content.append(self.content)
self.content = content
if data.get('embeds'):
embeds = data.get('embeds')
if embeds != self.embeds:
self.previous_embeds.append(self.embeds)
self.embeds = embeds
if data.get('tagged_everyone'):
tagged_everyone = data.get('tagged_everyone')
if self.tagged_everyone or tagged_everyone:
self.tagged_everyone = True
if data.get('tagged_users'):
tagged_users = data.get('tagged_users')
for user in tagged_users:
if user not in self.tagged_users:
self.tagged_users.append(user)
if data.get('tagged_roles'):
tagged_roles = data.get('tagged_roles')
for role in tagged_roles:
if role not in self.tagged_roles:
self.tagged_roles.append(role)
if data.get('tagged_channels'):
tagged_channels = data.get('tagged_channels')
for channel in tagged_channels:
if channel not in self.tagged_channels:
self.tagged_channels.append(channel)
self.save()
return create_success_response(self, status.HTTP_202_ACCEPTED, many=False)
@classmethod
def get_message_by_id(cls, id):
try:
return cls.objects.get(id=id)
except ObjectDoesNotExist:
return None
def __str__(self):
return (f'{self.created_at} | '
@ -48,11 +164,113 @@ class GuildInfo(models.Model):
class AdminRequest(models.Model):
guild = models.ForeignKey(Guild, on_delete=models.CASCADE)
author = models.ForeignKey(User, on_delete=models.CASCADE)
message = models.ForeignKey(Message, on_delete=models.CASCADE)
completed = models.BooleanField()
requested_at = models.DateTimeField()
completed_at = models.DateTimeField()
author = models.ForeignKey(User, on_delete=models.DO_NOTHING)
message = models.ForeignKey(Message, on_delete=models.DO_NOTHING)
channel = models.ForeignKey(Channel, on_delete=models.DO_NOTHING)
completed = models.BooleanField(default=False)
requested_at = models.DateTimeField(auto_now_add=True, blank=True)
completed_at = models.DateTimeField(null=True, blank=True, default=None)
completed_by = models.ForeignKey(User, on_delete=models.DO_NOTHING, null=True, blank=True, default=None)
completed_message = models.CharField(max_length=1000, null=True, blank=True, default=None)
content = models.CharField(max_length=2000)
def update_request(self, data):
completed = data.get('completed', False)
completed_by_id = data.get('completed_by')
completed_message = data.get('message')
if not self.completed and completed:
self.completed_at = datetime.now()
self.completed_message = completed_message
user = User.get_user_by_id(completed_by_id)
if not isinstance(user, User):
return create_error_response('User Does Not Exist',
status=status.HTTP_404_NOT_FOUND)
self.completed_by = user
self.save()
return create_request_success_response(self, status.HTTP_202_ACCEPTED)
@classmethod
def add_new_request(cls, data):
guild_id = data.get('guild')
author_id = data.get('author')
message_id = data.get('message')
channel_id = data.get('channel')
content = data.get('content')
if not (guild_id and author_id and message_id and channel_id and content):
return create_error_response("One or more of the required fields are missing.",
status=status.HTTP_400_BAD_REQUEST)
guild = Guild.get_guild_by_id(guild_id)
if not isinstance(guild, Guild):
return create_error_response('Guild Does Not Exist',
status=status.HTTP_404_NOT_FOUND)
author = User.get_author_by_id(author_id)
if not isinstance(author, User):
return create_error_response('Author Does Not Exist',
status=status.HTTP_404_NOT_FOUND)
message = Message.get_message_by_id(message_id)
if not isinstance(message, Message):
return create_error_response('Message Does Not Exist',
status=status.HTTP_404_NOT_FOUND)
channel = Channel.get_channel_by_id(channel_id)
if not isinstance(channel, Channel):
return create_error_response('Channel Does Not Exist',
status=status.HTTP_404_NOT_FOUND)
request = cls(
guild=guild,
author=author,
message=message,
channel=channel,
content=content
)
request.save()
return create_request_success_response(request, status.HTTP_201_CREATED, many=False)
@classmethod
def get_request_by_id(cls, id):
try:
return cls.objects.get(id=id)
except ObjectDoesNotExist:
return None
def __str__(self):
return f"{self.guild.id} | {self.requested_at} | By {self.author.id}"
class AdminComment(models.Model):
request = models.ForeignKey(AdminRequest, on_delete=models.CASCADE)
author = models.ForeignKey(User, on_delete=models.DO_NOTHING)
content = models.CharField(max_length=1000)
updated_at = models.DateTimeField(auto_now_add=True, blank=True)
@classmethod
def add_new_comment(cls, data):
request_id = data.get('request')
author_id = data.get('author')
content = data.get('content')
if not (request_id and author_id and content):
return create_error_response('Request, Author, and Content are required fields',
status=status.HTTP_400_BAD_REQUEST)
request = AdminRequest.get_request_by_id(request_id)
if not isinstance(request, AdminRequest):
return create_error_response("Admin Request Does Not Exist",
status=status.HTTP_404_NOT_FOUND)
author = User.get_user_by_id(author_id)
if not isinstance(author, User):
return create_error_response("Author Does Not Exist",
status=status.HTTP_404_NOT_FOUND)
comment = cls(
request=request,
author=author,
content=content
)
comment.save()
return create_comment_success_response(comment, status.HTTP_201_CREATED, many=False)
@classmethod
def get_comment_by_id(cls, id):
try:
return cls.objects.get(id=id)
except ObjectDoesNotExist:
return None

View File

@ -3,6 +3,7 @@ from rest_framework import serializers
from .models import Message
from .models import GuildInfo
from .models import AdminRequest
from .models import AdminComment
class MessageSerializer(serializers.ModelSerializer):
@ -10,6 +11,7 @@ class MessageSerializer(serializers.ModelSerializer):
model = Message
fields = "__all__"
class GuildInfoSerializer(serializers.ModelSerializer):
class Meta:
model = GuildInfo
@ -20,3 +22,9 @@ class AdminRequestSerializer(serializers.ModelSerializer):
class Meta:
model = AdminRequest
fields = "__all__"
class AdminCommentSerializer(serializers.ModelSerializer):
class Meta:
model = AdminComment
fields = "__all__"

View File

@ -0,0 +1,28 @@
from rest_framework.response import Response
from rest_framework import status
def create_error_response(msg, status=status.HTTP_404_NOT_FOUND):
return Response({'details': msg},
status=status)
def create_success_response(message_data, status, many: bool = False):
from .serializers import MessageSerializer
return Response(MessageSerializer(message_data, many=many).data,
status=status)
def create_request_success_response(request_data, status, many: bool = False):
from .serializers import AdminRequestSerializer
return Response(AdminRequestSerializer(request_data, many=many).data,
status=status)
def create_comment_success_response(comment_data, status, many: bool = False):
from .serializers import AdminCommentSerializer
return Response(AdminCommentSerializer(comment_data, many=many).data,
status=status)

View File

@ -1,8 +1,9 @@
from django.urls import path
from .views import GuildsAPI
from .views import GuildsAPI, GuildDetail
app_name = "users_api"
urlpatterns = [
path("/", view=GuildsAPI.as_view(), name="list")
path("/", view=GuildsAPI.as_view(), name="list"),
path("/<str:id>/", view=GuildDetail.as_view(), name='detail')
]

View File

@ -1,4 +1,4 @@
# Generated by Django 2.2.4 on 2019-09-16 05:23
# Generated by Django 2.2.4 on 2019-09-17 19:31
import django.contrib.postgres.fields
from django.db import migrations, models
@ -17,10 +17,10 @@ class Migration(migrations.Migration):
name='Guild',
fields=[
('id', models.CharField(max_length=30, primary_key=True, serialize=False)),
('admin_chat', models.CharField(max_length=30)),
('new_patron_message', models.TextField(blank=True, max_length=1000)),
('admin_chat', models.CharField(blank=True, max_length=30, null=True)),
('new_patron_message', models.TextField(blank=True, max_length=1000, null=True)),
('default_channel', models.CharField(max_length=30)),
('new_patron_channel', models.CharField(max_length=30)),
('new_patron_channel', models.CharField(blank=True, max_length=30, null=True)),
('prefixes', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=10), size=None)),
],
),

View File

@ -1,28 +0,0 @@
# Generated by Django 2.2.4 on 2019-09-17 05:08
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('guilds', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='guild',
name='admin_chat',
field=models.CharField(blank=True, max_length=30, null=True),
),
migrations.AlterField(
model_name='guild',
name='new_patron_channel',
field=models.CharField(blank=True, max_length=30, null=True),
),
migrations.AlterField(
model_name='guild',
name='new_patron_message',
field=models.TextField(blank=True, max_length=1000, null=True),
),
]

View File

@ -1,5 +1,14 @@
import os
from django.db import models
from django.contrib.postgres.fields import ArrayField
from django.core.exceptions import ObjectDoesNotExist
from rest_framework import status
from .utils import create_error_response
from .utils import create_success_response
from .utils import create_role_success_response
# Create your models here.
@ -15,11 +24,113 @@ class Guild(models.Model):
def __str__(self):
return self.id
def update_guild(self, data):
if data.get('admin_chat'):
self.admin_chat = data.get('admin_chat')
if data.get('new_patron_message'):
self.new_patron_message = data.get('new_patron_message')
if data.get('default_channel'):
self.default_channel = data.get('default_channel')
if data.get('new_patron_channel'):
self.new_patron_channel = data.get('new_patron_channel')
if data.get('add_prefix'):
if data.get('add_prefix') not in self.prefixes:
self.prefixes.append(data.get('add_prefix'))
if data.get('remove_prefix'):
if data.get('remove_prefix') in self.prefixes:
self.prefixes.remove(data.get('remove_prefix'))
if len(self.prefixes) <= 0:
self.prefixes = [os.environ['DISCORD_DEFAULT_PREFIX'], ]
self.save()
return self
@classmethod
def get_guild_by_id(cls, id):
try:
return cls.objects.get(id=id)
except ObjectDoesNotExist:
return None
@classmethod
def create_guild(cls, data):
id = data.get('id')
default_channel = data.get('default_channel')
if not (id and default_channel):
return create_error_response('id and default_channel are required',
status=status.HTTP_400_BAD_REQUEST)
if cls.get_guild_by_id(id):
return create_error_response('That Guild already exists',
status.HTTP_409_CONFLICT)
guild = cls(
id=id,
default_channel=default_channel,
prefixes=data.get('prefixes'),
admin_chat=data.get('admin_chat'),
new_patron_message=data.get('new_patron_message'),
new_patron_channel=data.get('new_patron_channel')
)
guild.save()
return create_success_response(guild, status.HTTP_201_CREATED, many=False)
class Role(models.Model):
id = models.CharField(max_length=30, primary_key=True)
guild = models.ForeignKey(Guild, on_delete=models.CASCADE, null=False)
type = models.PositiveSmallIntegerField()
role_type = models.PositiveSmallIntegerField()
def update_role(self, data):
if data.get('role_type'):
self.role_type = data.get('role_type')
self.save()
return create_role_success_response(self, status=status.HTTP_202_ACCEPTED, many=False)
@classmethod
def add_new_role(cls, data):
id = data.get('id')
guild_id = data.get('guild')
role_type = data.get('role_type')
if not (id and guild_id and role_type):
return create_error_response("The Role ID, Guild, and Role Type are required",
status=status.HTTP_400_BAD_REQUEST)
if cls.get_role_by_id(id):
return create_error_response("That Role Already Exists",
status=status.HTTP_409_CONFLICT)
guild = Guild.get_guild_by_id(guild_id)
if not isinstance(guild, Guild):
return create_error_response("Guild Does Not Exist",
status=status.HTTP_404_NOT_FOUND)
try:
role_type = int(role_type)
except ValueError:
return create_error_response("Role Type must be a positive number",
status=status.HTTP_400_BAD_REQUEST)
if role_type < 0:
return create_error_response("Role Type must be a positive number",
status=status.HTTP_400_BAD_REQUEST)
elif 1000 < role_type:
return create_error_response("Role Type must be less than 1000",
status=status.HTTP_400_BAD_REQUEST)
role = cls(
id=id,
guild=guild,
role_type=role_type
)
role.save()
return create_role_success_response(role, status.HTTP_201_CREATED, many=False)
@classmethod
def get_role_by_id(cls, id):
try:
return cls.objects.get(id=id)
except ObjectDoesNotExist:
return None
def __str__(self):
return f"{self.guild.id} | {self.id}"

View File

@ -0,0 +1,8 @@
from rest_framework.permissions import BasePermission
class GuildPermissions(BasePermission):
def has_permission(self, request, view):
return super().has_permission(request, view)
def has_object_permission(self, request, view, obj):
return super().has_object_permission(request, view, obj)

View File

@ -0,0 +1,21 @@
from rest_framework.response import Response
from rest_framework import status
def create_error_response(msg, status=status.HTTP_404_NOT_FOUND):
return Response({'details': msg},
status=status)
def create_success_response(guild_data, status, many: bool = False):
from .serializers import GuildSerializer
return Response(GuildSerializer(guild_data, many=many).data,
status=status)
def create_role_success_response(role_data, status, many: bool = False):
from .serializers import RoleSerializer
return Response(RoleSerializer(role_data, many=many).data,
status=status)

View File

@ -1,13 +1,12 @@
import os
from django.shortcuts import render
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import status
from rest_framework.permissions import IsAuthenticated
from django.core.exceptions import ObjectDoesNotExist
from geeksbot_v2.utils.api_utils import PaginatedAPIView
from .models import Guild
from .serializers import GuildSerializer
from .utils import create_error_response
from .utils import create_success_response
# Create your views here.
@ -15,37 +14,56 @@ from .serializers import GuildSerializer
class GuildsAPI(PaginatedAPIView):
def get(self, request, format=None):
users = Guild.objects.all()
page = self.paginate_queryset(users)
if page is not None:
serialized_users = GuildSerializer(users, many=True)
return self.get_paginated_response(serialized_users.data)
permission_classes = [IsAuthenticated]
serialized_users = GuildSerializer(users, many=True)
return Response(serialized_users.data)
def get(self, request, format=None):
guilds = Guild.objects.all()
page = self.paginate_queryset(guilds)
if page is not None:
return create_success_response(page, status.HTTP_200_OK, many=True)
return create_success_response(guilds, status.HTTP_200_OK, many=True)
def post(self, request, format=None):
data = dict(request.data)
print(data)
id = data.get('id')
default_channel = data.get('default_channel')
if not (id and default_channel):
return Response({'msg': 'id and default_channel are required'}, status=status.HTTP_400_BAD_REQUEST)
return Guild.create_guild(data)
admin_chat = data.get('admin_chat')
new_patron_message = data.get('new_patron_message')
default_prefix = os.environ['DISCORD_DEFAULT_PREFIX']
prefixes = data.get('prefixes', [default_prefix, ])
print(prefixes)
guild = Guild(
id=id[0] if isinstance(id, list) else id,
default_channel=default_channel[0] if isinstance(default_channel, list) else default_channel,
prefixes=prefixes,
admin_chat=admin_chat[0] if isinstance(admin_chat, list) else admin_chat,
new_patron_message=new_patron_message[0] if isinstance(new_patron_message, list) else new_patron_message
)
guild.save()
class GuildDetail(APIView):
permission_classes = [IsAuthenticated]
return Response(GuildSerializer(guild).data, status=status.HTTP_201_CREATED)
def get(self, request, id, format=None):
try:
guild = Guild.objects.get(id=id)
except ObjectDoesNotExist:
return create_error_response("Guild Does not Exist",
status=status.HTTP_404_NOT_FOUND)
else:
return create_success_response(guild,
status=status.HTTP_200_OK)
def put(self, request, id, format=None):
guild = Guild.get_guild_by_id(id)
if guild:
data = dict(request.data)
guild = guild.update_guild(data)
return create_success_response(guild,
status=status.HTTP_202_ACCEPTED)
else:
return create_error_response('Guild Does Not Exist',
status=status.HTTP_404_NOT_FOUND)
def delete(self, request, id, format=None):
guild = Guild.get_guild_by_id(id)
if guild:
# data = dict(request.data)
# TODO Add a check to verify user is allowed to delete...
# Possibly in object permissions...
guild.delete()
return create_success_response(guild,
status=status.HTTP_200_OK)
else:
return create_error_response('Guild Does Not Exist',
status=status.HTTP_404_NOT_FOUND)

View File

@ -1,4 +1,4 @@
# Generated by Django 2.2.4 on 2019-09-16 05:23
# Generated by Django 2.2.4 on 2019-09-17 19:31
from django.db import migrations, models
import django.db.models.deletion

View File

@ -1,15 +1,63 @@
from django.db import models
from django.core.exceptions import ObjectDoesNotExist
from rest_framework import status
from geeksbot_v2.guilds.models import Guild
from geeksbot_v2.guilds.models import Role
from .utils import create_error_response
from .utils import create_success_creator_response
from .utils import create_success_tier_response
# Create your models here.
class PatreonCreator(models.Model):
guild = models.ForeignKey(Guild, on_delete=models.CASCADE, null=False)
creator = models.CharField(max_length=50, null=False)
link = models.CharField(max_length=100, null=False)
guilds = models.ManyToManyField(Guild)
creator = models.CharField(max_length=50, null=False, primary_key=True)
link = models.CharField(max_length=100, null=False, unique=True)
def update_creator(self, data):
if data.get('guild'):
guild = Guild.get_guild_by_id(data.get('guild'))
if not isinstance(guild, Guild):
return create_error_response('Guild Does Not Exist',
status=status.HTTP_404_NOT_FOUND)
self.guilds.add(guild)
if data.get('link'):
self.link = data.get('link')
self.save()
return create_success_creator_response(self, status.HTTP_202_ACCEPTED, many=False)
@classmethod
def add_new_creator(cls, data):
creator = data.get('creator')
if PatreonCreator.get_creator_by_name(creator):
return create_error_response('That Creator already exists',
status=status.HTTP_409_CONFLICT)
link = data.get('link')
if not (creator and link):
return create_error_response('Creator and Link are both required fields',
status=status.HTTP_400_BAD_REQUEST)
guild = Guild.get_guild_by_id(data.get('guild'))
if not guild:
return create_error_response('A Valid Guild is required',
status=status.HTTP_400_BAD_REQUEST)
new_creator = cls(
creator=creator,
link=link
)
new_creator.save()
new_creator.guilds.add(guild)
return create_success_creator_response(new_creator, status.HTTP_201_CREATED, many=False)
@classmethod
def get_creator_by_name(cls, name):
try:
return cls.objects.get(creator=name)
except ObjectDoesNotExist:
return None
def __str__(self):
return f"{self.guild.id} | {self.creator}"
@ -17,11 +65,100 @@ class PatreonCreator(models.Model):
class PatreonTier(models.Model):
creator = models.ForeignKey(PatreonCreator, on_delete=models.CASCADE)
guild = models.ForeignKey(Guild, on_delete=models.CASCADE)
guild = models.ManyToManyField(Guild)
name = models.CharField(max_length=50)
description = models.TextField()
role = models.ForeignKey(Role, on_delete=models.CASCADE)
amount = models.IntegerField(null=True)
next_lower_tier = models.ForeignKey('self', null=True, blank=True)
def update_tier(self, data):
if data.get('guild'):
guild = Guild.get_guild_by_id(data.get('guild'))
if not isinstance(guild, Guild):
return create_error_response('Guild Does Not Exist',
status=status.HTTP_404_NOT_FOUND)
self.guilds.add(guild)
if data.get('name'):
self.name = data.get('name')
if data.get('description'):
self.description = data.get('description')
if data.get('role'):
role = Role.get_role_by_id(data.get('role'))
if not isinstance(role, Role):
return create_error_response('Role Does Not Exist',
status=status.HTTP_404_NOT_FOUND)
self.role = role
if data.get('amount'):
self.amount = data.get('amount')
if data.get('next_lower_tier'):
tier = self.get_tier_by_id(data.get('next_lower_tier'))
if not isinstance(tier, self.__class__):
return create_error_response('Next Lower Tier Does Not Exist',
status=status.HTTP_404_NOT_FOUND)
self.next_lower_tier = tier
self.save()
return create_success_tier_response(tier, status.HTTP_202_ACCEPTED, many=False)
@classmethod
def get_tier_by_id(cls, id):
try:
return cls.objects.get(id=id)
except ObjectDoesNotExist:
return None
@classmethod
def add_new_tier(cls, data):
creator_str = data.get('creator')
guild_id = data.get('guild')
name = data.get('name')
description = data.get('description')
role_id = data.get('role')
next_lower_tier_id = data.get('next_lower_tier')
if not (creator_str and guild_id and name and description and role_id):
return create_error_response("The Creator's name, Guild, Tier name, Description, "
"and Discord Role are all required.",
status=status.HTTP_400_BAD_REQUEST)
creator = PatreonCreator.get_creator_by_name(creator_str)
if not isinstance(creator, PatreonCreator):
return create_error_response("Creator Does Not Exist",
status=status.HTTP_404_NOT_FOUND)
guild = Guild.get_guild_by_id(guild_id)
if not isinstance(guild, Guild):
return create_error_response("Guild Does Not Exist",
status=status.HTTP_404_NOT_FOUND)
role = Role.get_role_by_id(role_id)
if not isinstance(role, Role):
return create_error_response("Role Does Not Exist",
status=status.HTTP_404_NOT_FOUND)
if next_lower_tier_id:
next_lower_tier = cls.get_tier_by_id(next_lower_tier_id)
if not isinstance(next_lower_tier, cls):
return create_error_response("Next Lower Tier Does Not Exist",
status=status.HTTP_404_NOT_FOUND)
if next_lower_tier.guild != guild:
return create_error_response("The given next lower tier is not for the same guild.",
status=status.HTTP_400_BAD_REQUEST)
if next_lower_tier.creator != creator:
return create_error_response("The given next lower tier is not for the same creator.",
status=status.HTTP_400_BAD_REQUEST)
try:
PatreonTier.objects.filter(creator=creator, guilds__id=guild.id).get(name=name)
except ObjectDoesNotExist:
tier = cls(
creator=creator,
name=name,
description=description,
role=role,
amount=data.get('amount'),
next_lower_tier=next_lower_tier if next_lower_tier_id else None
)
tier.save()
return create_success_tier_response(tier, status.HTTP_201_CREATED, many=False)
else:
return create_error_response("A Tier with that name already exists for that creator in this guild.",
status=status.HTTP_409_CONFLICT)
def __str__(self):
return f"{self.guild.id} | {self.creator.creator} | {self.name}"

View File

@ -0,0 +1,21 @@
from rest_framework.response import Response
from rest_framework import status
def create_error_response(msg, status=status.HTTP_404_NOT_FOUND):
return Response({'details': msg},
status=status)
def create_success_creator_response(creator_data, status, many: bool = False):
from .serializers import PatreonCreatorSerializer
return Response(PatreonCreatorSerializer(creator_data, many=many).data,
status=status)
def create_success_tier_response(tier_data, status, many: bool = False):
from .serializers import PatreonTierSerializer
return Response(PatreonTierSerializer(tier_data, many=many).data,
status=status)

View File

@ -1,4 +1,4 @@
# Generated by Django 2.2.4 on 2019-09-16 05:23
# Generated by Django 2.2.4 on 2019-09-17 19:31
from django.conf import settings
from django.db import migrations, models
@ -10,9 +10,9 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('guilds', '0001_initial'),
('dmessages', '0001_initial'),
('guilds', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [

View File

@ -1,8 +1,13 @@
from django.db import models
from django.core.exceptions import ObjectDoesNotExist
from rest_framework import status
from geeksbot_v2.guilds.models import Guild
from geeksbot_v2.dmessages.models import Message
from geeksbot_v2.users.models import User
from geeksbot_v2.channels.models import Channel
from .utils import create_error_response
from .utils import create_success_response
# Create your models here.
@ -14,16 +19,110 @@ class RconServer(models.Model):
port = models.PositiveIntegerField()
password = models.CharField(max_length=50)
monitor_chat = models.BooleanField()
monitor_chat_channel = models.CharField(max_length=30, blank=True)
alerts_channel = models.CharField(max_length=30, blank=True)
info_channel = models.CharField(max_length=30, blank=True)
monitor_chat_channel = models.ForeignKey(
Channel, on_delete=models.DO_NOTHING, related_name="+", null=True, blank=True, default=None
)
alerts_channel = models.ForeignKey(
Channel, on_delete=models.DO_NOTHING, related_name="+", null=True, blank=True, default=None
)
info_channel = models.ForeignKey(
Channel, on_delete=models.DO_NOTHING, related_name="+", null=True, blank=True, default=None
)
info_message = models.ForeignKey(
Message, on_delete=models.CASCADE, related_name="+", blank=True
Message, on_delete=models.DO_NOTHING, related_name="+", null=True, blank=True, default=None
)
settings_message = models.ForeignKey(
Message, on_delete=models.CASCADE, related_name="+", blank=True
Message, on_delete=models.DO_NOTHING, related_name="+", null=True, blank=True, default=None
)
whitelist = models.ManyToManyField(User, blank=True)
def update_server(self, data):
if data.get('name'):
self.name = data.get('name')
if data.get('ip'):
self.ip = data.get('ip')
if data.get('port'):
self.port = data.get('port')
if data.get('password'):
self.password = data.get('password')
if data.get('monitor_chat'):
self.monitor_chat = data.get('monitor_chat')
if 'monitor_chat_channel' in data.keys():
self.monitor_chat_channel = Channel.get_channel_by_id(data.get('monitor_chat_channel'))
if 'alerts_channel' in data.keys():
self.alerts_channel = Channel.get_channel_by_id(data.get('alerts_channel'))
if 'info_channel' in data.keys():
self.alerts_channel = Channel.get_channel_by_id(data.get('info_channel'))
if 'info_message' in data.keys():
self.info_message = Message.get_message_by_id(data.get('info_message'))
if 'settings_message' in data.keys():
self.settings_message = Message.get_message_by_id(data.get('settings_message'))
self.save()
return create_success_response(self, status.HTTP_202_ACCEPTED, many=False)
def add_whitelist(self, user_id):
user = User.get_user_by_id(user_id)
if not isinstance(user, User):
return create_error_response("User Does Not Exist",
status=status.HTTP_404_NOT_FOUND)
if not user.steam_id:
return create_error_response("User does not have a Steam 64ID attached to their account",
status=status.HTTP_406_NOT_ACCEPTABLE)
self.whitelist.add(user)
return create_error_response("User has been added to the whitelist",
status=status.HTTP_200_OK)
def remove_from_whitelist(self, user_id):
user = User.get_user_by_id(user_id)
if not isinstance(user, User):
return create_error_response("User Does Not Exist",
status=status.HTTP_404_NOT_FOUND)
self.whitelist.remove(user)
return create_error_response("User has been removed from the whitelist",
status=status.HTTP_200_OK)
@classmethod
def add_new_server(cls, data):
guild_id = data.get('guild')
name = data.get('name')
ip = data.get('ip')
port = data.get('port')
password = data.get('password')
if not (guild_id and name and ip and port and password):
return create_error_response("One or more of the required fields are missing",
status=status.HTTP_400_BAD_REQUEST)
guild = Guild.get_guild_by_id(guild_id)
if not isinstance(guild, Guild):
return create_error_response("Guild Does Not Exist",
status=status.HTTP_404_NOT_FOUND)
server = cls(
guild=guild,
name=name,
ip=ip,
port=port,
password=password,
monitor_chat=data.get('monitor_chat', False)
)
server.save()
return create_success_response(server, status.HTTP_201_CREATED, many=False)
@classmethod
def get_server(cls, guild_id, name):
guild_servers = cls.get_guild_servers(guild_id)
if guild_servers:
try:
return guild_servers.get(name=name)
except ObjectDoesNotExist:
return None
return None
@classmethod
def get_guild_servers(cls, guild_id):
guild = Guild.get_guild_by_id(guild_id)
if not isinstance(guild, guild):
return None
return cls.objects.filter(guild=guild)
def __str__(self):
return f"{self.guild.id} | {self.name}"

14
geeksbot_v2/rcon/utils.py Normal file
View File

@ -0,0 +1,14 @@
from rest_framework.response import Response
from rest_framework import status
def create_error_response(msg, status=status.HTTP_404_NOT_FOUND):
return Response({'details': msg},
status=status)
def create_success_response(rcon_data, status, many: bool = False):
from .serializers import RconServerSerializer
return Response(RconServerSerializer(rcon_data, many=many).data,
status=status)

View File

@ -13,7 +13,7 @@
{% if object.name %}
<p>{{ object.name }}</p>
{% endif %}
{{ user.id }}
{{ user.auth_token }}
</div>
</div>

View File

@ -1,13 +1,17 @@
from django.contrib import admin
from django.contrib.auth import admin as auth_admin
from .forms import UserChangeForm
from .forms import UserChangeForm, UserCreateForm
from .models import User
class UserAdmin(auth_admin.UserAdmin):
model = User
form = UserChangeForm
add_form = UserCreateForm
add_fieldsets = auth_admin.UserAdmin.add_fieldsets + (
(None, {'fields': ('id')}),
)
admin.site.register(User, UserAdmin)

View File

@ -1,8 +1,9 @@
from django.urls import path
from geeksbot_v2.users.views import UsersAPI
from geeksbot_v2.users.views import UsersAPI, UserDetail
app_name = "users_api"
urlpatterns = [
path("users/", view=UsersAPI.as_view(), name="list")
path("/", view=UsersAPI.as_view(), name="list"),
path("/<str:id>/", view=UserDetail.as_view(), name="detail"),
]

View File

@ -1,8 +1,20 @@
from django.contrib.auth import forms
from django.forms import CharField
from allauth.account.forms import SignupForm
from .models import User
class UserCreateForm(SignupForm):
id = CharField(max_length=30, label='Discord ID')
def save(self, request):
user = super(UserCreateForm, self).save(request)
user.id = self.cleaned_data['id']
user.save()
return user
class UserChangeForm(forms.UserChangeForm):
class Meta(forms.UserChangeForm.Meta):
model = User

View File

@ -0,0 +1,70 @@
# Generated by Django 2.2.4 on 2019-09-17 19:38
from django.conf import settings
import django.contrib.auth.models
import django.contrib.auth.validators
import django.contrib.postgres.fields
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
initial = True
dependencies = [
('auth', '0011_update_proxy_permissions'),
('guilds', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='User',
fields=[
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
('first_name', models.CharField(blank=True, max_length=30, verbose_name='first name')),
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
('name', models.CharField(blank=True, max_length=255, verbose_name='Name of User')),
('id', models.CharField(max_length=30, primary_key=True, serialize=False)),
('discord_username', models.CharField(max_length=100, null=True)),
('previous_discord_usernames', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=100), blank=True, null=True, size=None)),
('discriminator', models.IntegerField(null=True)),
('previous_discriminators', django.contrib.postgres.fields.ArrayField(base_field=models.IntegerField(), blank=True, null=True, size=None)),
('steam_id', models.CharField(blank=True, max_length=30, null=True)),
('animated', models.BooleanField(blank=True, null=True)),
('avatar', models.CharField(blank=True, max_length=100, null=True)),
('bot', models.BooleanField(blank=True, null=True)),
('banned', models.BooleanField(default=False)),
('logging_enabled', models.BooleanField(default=True)),
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')),
('guilds', models.ManyToManyField(blank=True, null=True, to='guilds.Guild')),
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')),
],
options={
'verbose_name': 'user',
'verbose_name_plural': 'users',
'abstract': False,
},
managers=[
('objects', django.contrib.auth.models.UserManager()),
],
),
migrations.CreateModel(
name='UserLog',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('time', models.DateTimeField()),
('action', models.IntegerField()),
('description', models.CharField(max_length=100)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
]

View File

@ -0,0 +1,19 @@
# Generated by Django 2.2.4 on 2019-09-17 21:09
import django.contrib.auth.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('users', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='user',
name='username',
field=models.CharField(help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username'),
),
]

View File

@ -0,0 +1,24 @@
# Generated by Django 2.2.4 on 2019-09-18 05:54
import django.contrib.postgres.fields
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('users', '0002_auto_20190917_2109'),
]
operations = [
migrations.AlterField(
model_name='user',
name='discriminator',
field=models.CharField(max_length=4, null=True),
),
migrations.AlterField(
model_name='user',
name='previous_discriminators',
field=django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=4), blank=True, null=True, size=None),
),
]

View File

View File

@ -1,4 +1,5 @@
from django.contrib.auth.models import AbstractUser
from django.contrib.auth.validators import UnicodeUsernameValidator
from django.db.models import CharField
from django.urls import reverse
from django.utils.translation import ugettext_lazy as _
@ -8,8 +9,14 @@ from django.conf import settings
from django.db.models.signals import post_save
from django.dispatch import receiver
from rest_framework.authtoken.models import Token
from django.core.exceptions import ObjectDoesNotExist
from rest_framework import status
from geeksbot_v2.guilds.models import Guild
from .utils import verify_user_data
from .utils import create_error_response
from .utils import create_log_success_response
from .utils import create_success_response
@receiver(post_save, sender=settings.AUTH_USER_MODEL)
@ -23,11 +30,18 @@ class User(AbstractUser):
# First Name and Last Name do not cover name patterns
# around the globe.
name = CharField(_("Name of User"), blank=True, max_length=255)
username = models.CharField(
_('username'),
max_length=150,
unique=False,
help_text=_('Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.'),
validators=[UnicodeUsernameValidator()],
)
id = models.CharField(max_length=30, primary_key=True)
discord_username = models.CharField(max_length=100, null=True)
previous_discord_usernames = ArrayField(models.CharField(max_length=100), blank=True, null=True)
discriminator = models.IntegerField(null=True)
previous_discriminators = ArrayField(models.IntegerField(), blank=True, null=True)
discriminator = models.CharField(max_length=4, null=True)
previous_discriminators = ArrayField(models.CharField(max_length=4), blank=True, null=True)
guilds = models.ManyToManyField(Guild, blank=True, null=True)
steam_id = models.CharField(max_length=30, blank=True, null=True)
animated = models.BooleanField(blank=True, null=True)
@ -36,15 +50,158 @@ class User(AbstractUser):
banned = models.BooleanField(default=False)
logging_enabled = models.BooleanField(default=True)
@classmethod
def add_new_user(cls, data):
if not verify_user_data(data):
return create_error_response("Not all required fields are present.",
status=status.HTTP_400_BAD_REQUEST)
id = data.get('id')
if id:
if User.objects.filter(id=id).exists():
return create_error_response("User Exists please update instead of create",
status=status.HTTP_409_CONFLICT)
discord_username = data.get('username')
discriminator = data.get('discriminator')
guild_id = data.get('guild')
try:
guild = Guild.objects.get(id=str(guild_id))
except ObjectDoesNotExist:
return create_error_response("That is not a valid Guild",
status=status.HTTP_400_BAD_REQUEST)
animated = data.get('animated')
avatar = data.get('avatar')
bot = data.get('bot')
banned = data.get('banned')
logging = data.get('logging')
if not (avatar and (animated is not None) and (bot is not None)):
return create_error_response("All required fields must contain a value",
status.HTTP_400_BAD_REQUEST)
user = User(
id=id,
discord_username=discord_username,
discriminator=discriminator,
animated=animated,
avatar=avatar,
bot=bot,
banned=banned or False,
logging_enabled=logging or True
)
user.save()
user.guilds.add(guild)
return create_success_response(user, status.HTTP_201_CREATED, many=False)
def update_user(self, data):
if data.get('username') and data.get('username') != self.discord_username:
if isinstance(self.previous_discord_usernames, list):
self.previous_discord_usernames.append(self.discord_username)
else:
self.previous_discord_usernames = [self.discord_username, ]
self.discord_username = data.get('username')
if data.get('discriminator') and data.get('discriminator') != self.discriminator:
if isinstance(self.previous_discriminators, list):
self.previous_discriminators.append(self.discriminator)
else:
self.previous_discriminators = [self.discriminator, ]
self.discriminator = data.get('discriminator')
if data.get('guild'):
guild = Guild.get_guild_by_id(data.get('guild'))
if not isinstance(guild, Guild):
return create_error_response("That is not a valid Guild",
status=status.HTTP_400_BAD_REQUEST)
self.guilds.add(guild)
if data.get('steam_id'):
self.steam_id = data.get('steam_id')
if data.get('animated'):
self.animated = data.get('animated')
if data.get('avatar'):
self.avatar = data.get('avatar')
if data.get('bot'):
self.bot = data.get('bot')
if data.get('banned'):
self.banned = data.get('banned')
if data.get('logging'):
self.logging_enabled = data.get('logging')
self.save()
return create_success_response(self, status.HTTP_202_ACCEPTED, many=False)
@classmethod
def get_user_by_id(cls, id):
try:
return cls.objects.get(id=id)
except ObjectDoesNotExist:
return None
def get_absolute_url(self):
return reverse("users:detail", kwargs={"username": self.username})
class UserLog(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
time = models.DateTimeField()
time = models.DateTimeField(auto_now_add=True, blank=True)
action = models.IntegerField()
description = models.CharField(max_length=100)
description = models.CharField(max_length=100, null=True, blank=True)
@classmethod
def add_new_log(cls, user, data):
user_id = data.get('user')
action = data.get('action')
description = data.get('description')
if not (user_id and action):
return create_error_response("User and Action are required.",
status=status.HTTP_400_BAD_REQUEST)
user = User.get_user_by_id(user_id)
if not isinstance(user, User):
return create_error_response("User Does Not Exist",
status=status.HTTP_404_NOT_FOUND)
try:
action = int(action)
except ValueError:
return create_error_response("The Action must be a number",
status=status.HTTP_400_BAD_REQUEST)
log = cls(
user=user,
action=action,
description=description
)
log.save()
return create_log_success_response(log, status.HTTP_201_CREATED, many=False)
@classmethod
def get_log_by_id(cls, id):
try:
return cls.objects.get(id=id)
except ObjectDoesNotExist:
return None
@classmethod
def get_logs_by_user(cls, user_id, count: int = None):
user = User.get_user_by_id(user_id)
if isinstance(user, User):
user_logs = cls.objects.filter(user=user).order_by('-time')
if count:
user_logs = user_logs[:count]
if len(user_logs) > 0:
return user_logs
else:
return []
else:
return []
@classmethod
def get_logs_by_user_action(cls, user_id, action, count: int = None):
user = User.get_user_by_id(user_id)
if isinstance(user, User):
user_logs = cls.objects.filter(user=user, action=action).order_by('-time')
if count:
user_logs = user_logs[:count]
if len(user_logs) > 0:
return user_logs
else:
return []
else:
return []
def __str__(self):
return f"{self.time} | {self.user.id} | {self.action}"

View File

@ -7,7 +7,22 @@ from geeksbot_v2.users.models import UserLog
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = "__all__"
fields = [
'id',
'username',
'name',
'discord_username',
'previous_discord_usernames',
'discriminator',
'previous_discriminators',
'guilds',
'steam_id',
'animated',
'avatar',
'bot',
'banned',
'logging_enabled'
]
class UserLogSerializer(serializers.ModelSerializer):

View File

@ -0,0 +1,36 @@
from rest_framework.response import Response
from rest_framework import status
def create_error_response(msg, status=status.HTTP_404_NOT_FOUND):
return Response({'details': msg},
status=status)
def create_success_response(user_data, status, many: bool = False):
from .serializers import UserSerializer
return Response(UserSerializer(user_data, many=many).data,
status=status)
def create_log_success_response(log_data, status, many: bool = False):
from .serializers import UserLogSerializer
return Response(UserLogSerializer(log_data, many=many).data,
status=status)
required_fields = [
'id',
'username',
'discriminator',
'guild',
'animated',
'avatar',
'bot',
]
def verify_user_data(data):
return all([field in data.keys() for field in required_fields])

View File

@ -1,19 +1,19 @@
from django.contrib.auth import get_user_model
from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse
from django.views.generic import DetailView, RedirectView, UpdateView
from django.contrib import messages
from django.utils.translation import ugettext_lazy as _
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated
from rest_framework import status
from geeksbot_v2.users.serializers import UserSerializer
from geeksbot_v2.users.serializers import UserLogSerializer
from geeksbot_v2.users.models import UserLog
from .models import UserLog
from geeksbot_v2.utils.api_utils import PaginatedAPIView
User = get_user_model()
from .models import User
from .utils import create_error_response
from .utils import create_success_response
from .utils import create_log_success_response
class UserDetailView(LoginRequiredMixin, DetailView):
@ -24,6 +24,7 @@ class UserDetailView(LoginRequiredMixin, DetailView):
def get(self, request, *args, **kwargs):
self.object = self.get_object()
context = self.get_context_data(object=self.object, user=request.user)
return self.render_to_response(context)
@ -66,44 +67,72 @@ user_redirect_view = UserRedirectView.as_view()
class UsersAPI(PaginatedAPIView):
def get(self, request, guild, format=None):
users = User.objects.filter(guilds__id=guild)
permission_classes = [IsAuthenticated]
def get(self, request, guild=None, format=None):
if guild:
users = User.objects.filter(guilds__id=guild)
else:
users = User.objects.all()
page = self.paginate_queryset(users)
if page is not None:
serialized_users = UserSerializer(users, many=True)
return self.get_paginated_response(serialized_users.data)
return create_success_response(page, status.HTTP_200_OK, many=True)
serialized_users = UserSerializer(users, many=True)
return Response(serialized_users.data)
return create_success_response(users, status.HTTP_200_OK, many=True)
def post(self, request, format=None):
data = dict(request.data)
return User.add_new_user(data)
class UserDetail(APIView):
def get(self, request, guild, id, format=None):
user = User.objects.filter(guilds__id=guild).get(id=id)
return Response(UserSerializer(user).data)
permission_classes = [IsAuthenticated]
def get(self, request, id, format=None):
user = User.get_user_by_id(id)
if not isinstance(user, User):
return create_error_response("User Does not Exist",
status=status.HTTP_404_NOT_FOUND)
return create_success_response(user,
status=status.HTTP_200_OK)
def put(self, request, id, format=None):
user = User.get_user_by_id(id)
if isinstance(user, User):
data = dict(request.data)
return user.update_user(data)
else:
return create_error_response("User Does Not Exist",
status=status.HTTP_404_NOT_FOUND)
class UserLogList(PaginatedAPIView):
permission_classes = [IsAuthenticated]
def get(self, request, user, action=None, format=None):
if action:
user_logs = (
UserLog.objects.filter(user=user)
.filter(action=action)
.order_by("-time")
)
user_logs = UserLog.get_logs_by_user_action(user, action)
else:
user_logs = UserLog.objects.filter(user=user).order_by("-time")
user_logs = UserLog.get_logs_by_user(user)
page = self.paginate_queryset(user_logs)
if page is not None:
serialized_logs = UserLogSerializer(page, many=True)
return self.get_paginated_response(serialized_logs.data)
return create_log_success_response(page, status.HTTP_200_OK, many=True)
serialized_logs = UserLogSerializer(user_logs, many=True)
return Response(serialized_logs.data)
return create_log_success_response(user_logs, status.HTTP_200_OK, many=True)
def post(self, request, user, format=None):
data = dict(request.data)
return UserLog.add_new_log(user, data)
class UserLogDetail(APIView):
permission_classes = [IsAuthenticated]
def get(self, request, id, format=None):
user_log = UserLog.objects.get(id=id)
return Response(UserLogSerializer(user_log).data)
user_log = UserLog.get_log_by_id(id)
if isinstance(user_log, UserLog):
return create_log_success_response(user_log, status.HTTP_200_OK, many=False)
else:
return create_error_response("Log Does Not Exist",
status=status.HTTP_404_NOT_FOUND)

View File

@ -61,7 +61,7 @@ listen_addresses = '*'
# defaults to 'localhost'; use '*' for all
# (change requires restart)
#port = 5432 # (change requires restart)
#max_connections = 100 # (change requires restart)
max_connections = 1000 # (change requires restart)
#superuser_reserved_connections = 3 # (change requires restart)
#unix_socket_directories = '/tmp' # comma-separated list of directories
# (change requires restart)