from django.core.management.base import BaseCommand, CommandError
from core import models as cm

import argparse
import datetime
import sys

class Command(BaseCommand):
    help = """
    A DANGEROUS utility for editing the DB to fix known issues due to botched migrations, etc.
    ALWAYS run in display mode before running in fix mode
    Display or repair depending on mode
    usage: python manage.py fix_db -t <trouble type> -m <mode>
    """

    def handle(self, *args, **options):
        trouble_type = options["troubletype"]
        mode = options["mode"]

        if mode == "fix":
            if trouble_type=="abstract_items":
                for o in cm.ParticipationItem.objects.all():
                    try:
                        i = o.get_inherited_instance()
                    except:
                        sys.stdout.write("participation item found whose inherited instance can't be determined:{}".format(o.name)+"\n")
                        sys.stdout.flush()
                        sys.stdout.write("deleting item\n")
                        o.delete()
                        sys.stdout.write("object deleted\n")

            elif trouble_type=="abstract_projects":
                num = 0
                for o in cm.ParticipationProject.objects.all():
                    num += 1
                    if num % 1000 == 0:
                        sys.stdout.write("{}\n".format(num))
                    try:
                        i = o.get_inherited_instance()
                    except:
                        sys.stdout.write("participation project found whose inherited instance can't be determined:{}".format(o.name)+"\n")
                        sys.stdout.flush()
                        sys.stdout.write("deleting project\n")
                        o.delete()
                        sys.stdout.write("object deleted\n")

            elif trouble_type=="clear_inactive":
                items_deleted = cm.ParticipationItem.objects.filter(is_active=False).delete()
                projects_deleted = cm.ParticipationProject.objects.filter(is_active=False).delete()
                sys.stdout.write("Number of items deleted:{}, number of projects deleted: {}\n".format(items_deleted, projects_deleted))
                sys.stdout.flush()

            else:
                raise Exception("unknown trouble type:"+str(trouble_type))
        elif mode == "display":
            if trouble_type=="abstract_items":
                for o in cm.ParticipationItem.objects.all():
                    try:
                        i = o.get_inherited_instance()
                    except:
                        sys.stdout.write("participation item found whose inherited instance can't be determined:{}".format(o.name)+"\n")
                        sys.stdout.flush()

            elif trouble_type=="abstract_projects":
                num = 0
                for o in cm.ParticipationProject.objects.all().iterator():
                    num += 1
                    if num % 1000 == 0:
                        sys.stdout.write("{}\n".format(num))
                    try:
                        i = o.get_inherited_instance()
                    except:
                        sys.stdout.write("participation project found whose inherited instance can't be determined:{}".format(o.name)+"\n")
                        sys.stdout.flush()

            elif trouble_type=="clear_inactive":
                items_deleted = cm.ParticipationItem.objects.filter(is_active=False).count()
                projects_deleted = cm.ParticipationProject.objects.filter(is_active=False).count()
                sys.stdout.write("Number of inactive items to delete:{}, number of inactive projects to delete: {}\n".format(items_deleted, projects_deleted))
                sys.stdout.flush()

            else:
                raise Exception("unknown trouble type:"+str(trouble_type))
        else:
            raise Exception("unknown mode:"+str(mode))
        sys.stdout.write("DONE\n")
        sys.stdout.flush()

    def add_arguments(self, parser):
        parser.add_argument('-m', '--mode', required=True, type=str, help="mode", action='store')
        parser.add_argument('-t', '--troubletype', required=True, type=str, help="trouble type", action='store')


# 0509.py
import cv2
import numpy as np

src = cv2.imread('./data/lena.jpg')
cv2.imshow('src',  src)

#1
hsv    = cv2.cvtColor(src, cv2.COLOR_BGR2HSV)
h, s, v = cv2.split(hsv)

v2 = cv2.equalizeHist(v)
hsv2 = cv2.merge([h, s, v2])
dst    = cv2.cvtColor(hsv2, cv2.COLOR_HSV2BGR)
cv2.imshow('dst',  dst)

#2
yCrCv = cv2.cvtColor(src, cv2.COLOR_BGR2YCrCb)
y, Cr, Cv = cv2.split(yCrCv)

y2 = cv2.equalizeHist(y)
yCrCv2 = cv2.merge([y2, Cr, Cv])
dst2    = cv2.cvtColor(yCrCv2, cv2.COLOR_YCrCb2BGR)

cv2.imshow('dst2',  dst2)
cv2.waitKey()
cv2.destroyAllWindows()


from typing import Optional
from django.core.management.base import BaseCommand, CommandParser
from datetime import datetime, timezone
import json
from pathlib import Path

from replays import constant_helpers
from replays import replay_parsing
from replays import create_replay
from replays import limits
from replays import models

ROYALFLARE_JSON_DIRECTORY_PATH = Path(__file__).parent / "resources" / "royalflare"

class Command(BaseCommand):
    help = """Import royalflare replays. To use this, you must first download and
    extract royalflare replays from https://maribelhearn.com/mirror/royalflare.zip.
    Then, run this command with an argument pointing to the /replays folder you just
    made. The script imports those replays, using additional data from jsons that
    were downloaded from
    https://github.com/MaribelHearn/maribelhearn.com/tree/master/assets/games/royalflare/json
    """

    def add_arguments(self, parser: CommandParser) -> None:
        super().add_arguments(parser)

        parser.add_argument(
            "replay_dir",
            help="The directory containing the replays. Should end in /replays.",
        )

        parser.add_argument(
            "--json",
            help="A path to royalflare json file containing info for replays to "
            + "import.\nIf not set, all jsons will be used.",
        )

    def handle(self, *args, **options):
        main(options["replay_dir"], options["json"])

def main(replay_dir: str, json_file_arg: Optional[str]) -> None:
    if json_file_arg is not None:
        json_files = [json_file_arg]
    else:
        json_files = ROYALFLARE_JSON_DIRECTORY_PATH.glob("**/*.json")

    for json_file in json_files:
        with open(json_file, encoding="utf-8") as f:
            all_replay_infos_from_json = json.load(f)
            for info_from_json in all_replay_infos_from_json:
                import_royalflare(info_from_json, Path(replay_dir))

def import_royalflare(info_from_json: dict, replay_dir: Path) -> None:
    comment = info_from_json["comment"]
    replay_path = parse_replay_path_from_json(replay_dir, info_from_json["replay"])
    if "uploaded" in info_from_json:
        created_timestamp = parse_timestamp_from_json(info_from_json["uploaded"])
    else:
        # The first 3 replays ever uploaded on rf do not have an 'uploaded' timestamp.
        # They can be found at the bottom of th08.json
        created_timestamp = parse_timestamp_from_json(info_from_json["date"])
    imported_username = info_from_json["player"]

    try:
        if replay_path.stat().st_size > limits.MAX_REPLAY_SIZE:
            raise limits.FileTooBigError()
        replay_bytes = replay_path.read_bytes()
        duplicate = constant_helpers.GetReplayFileWithSameHash(replay_bytes)
        if duplicate is not None:
            raise Exception("This replay already exists")
        replay_info = replay_parsing.Parse(replay_bytes)
        temp_replay = models.TemporaryReplayFile(user=None, replay=replay_bytes)
        temp_replay.save()

        create_replay.PublishNewReplay(
            user=None,
            difficulty=replay_info.difficulty,
            score=replay_info.score,
            category=1,
            comment=comment,
            is_good=True,
            is_clear=True,
            no_bomb=False,
            miss_count=None,
            video_link="",
            temp_replay_instance=temp_replay,
            replay_info=replay_info,
            created_timestamp=created_timestamp,
            imported_username=imported_username,
        )
    except Exception as e:
        if str(e) != "This replay already exists":
            print(f"Failed to import {replay_path}")
            print(e)

def parse_replay_path_from_json(replay_directory: Path, replay_location: str) -> Path:
    path_including_top_level_dir = Path(replay_location.lstrip("/"))
    path = Path(*path_including_top_level_dir.parts[1:])
    return replay_directory / path

def parse_timestamp_from_json(timestamp: str) -> datetime:
    if ":" in timestamp:
        unaware_datetime = datetime.strptime(timestamp, "%Y/%m/%d %H:%M:%S")
    else:
        unaware_datetime = datetime.strptime(timestamp, "%Y/%m/%d")
    aware_datetime = unaware_datetime.replace(tzinfo=timezone.utc)
    return aware_datetime


import app.vk_commands as VKC
import datetime
from app.extensions import db
from app.models import Player

def check_name(name):
    isExist = True

    player = Player.query.filter_by(name=name).first()

    if not player:
        isExist = False

    return isExist

def addPlayer(data):

    message = 'Прости, но ты уже есть в списке игроков.\n\n'

    user_id = data['from_id']

    player = Player.query.filter_by(user_id=user_id).first()

    if not player:

        # получаем имя, так как оно последнее и без пробелов
        name = data['text'].split()[-1]

        if check_name(name):
            message = 'Такое имя уже есть в списке.\n\n'
            return message, ''

        date = datetime.datetime.today() + datetime.timedelta(hours=3)

        player = Player(user_id=user_id, name=name, date=date)
        db.session.add(player)
        db.session.commit()

        message = 'Я добавил тебя в таблицу.\n\n'

    players = Player.query.all()

    for player in players:
        message += player.name + " - " + player.date.strftime("%d/%m/%Y") + "\n"

    return message, ''

addPlayer_command = VKC.Command()
addPlayer_command.keys = ['Адик, добавь меня, я ']
addPlayer_command.desription = 'Добавляет игрока в таблицу'
addPlayer_command.proccess = addPlayer


import cv2
import numpy as np
import face_recognition
import os
from handle_sheet_index import update_col_index, get_current_index
# from PIL import ImageGrab
from google_sheet import invoke_present, get_sheet_name
import sys

# Finding the current sheet column index
# current_index = get_current_index()

def prepare_images(section):
    path = f'images/{section}'
    images = []
    classNames = []
    myList = os.listdir(path)
    print(myList)
    for cl in myList:
        curImg = cv2.imread(f'{path}/{cl}')
        images.append(curImg)
        classNames.append(tuple(os.path.splitext(cl)[0].split(" ")))
        print(classNames)
    return images,classNames

def findEncodings(images):
    encodeList = []
    for img in images:
        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        encode = face_recognition.face_encodings(img)[0]
        encodeList.append(encode)
    return encodeList

def markAttendance(student_id,current_index):
    invoke_present(student_id,current_index)

def attendence_process(instruct,section,subject,col_idx):
    get_sheet_name(subject) #set the sheet name
    print("starting attendence process")
    cap = cv2.VideoCapture(0,cv2.CAP_DSHOW)
    print("yeea",section)

    if instruct==None:

        print("inside if")
        images, classNames = prepare_images(section)
        encodeListKnown = findEncodings(images)
        attended_student = []

        while True:
            success, img = cap.read()
            #img = captureScreen()
            imgS = cv2.resize(img,(0,0),None,0.25,0.25)
            imgS = cv2.cvtColor(imgS, cv2.COLOR_BGR2RGB)

            facesCurFrame = face_recognition.face_locations(imgS)
            encodesCurFrame = face_recognition.face_encodings(imgS,facesCurFrame)

            for encodeFace,faceLoc in zip(encodesCurFrame,facesCurFrame):
                matches = face_recognition.compare_faces(encodeListKnown,encodeFace)
                # print(f"matches: {matches}")
                faceDis = face_recognition.face_distance(encodeListKnown,encodeFace)
                # print("facedistance",faceDis)
                matchIndex = np.argmin(faceDis)
                # print("distance value",faceDis[matchIndex])
                if faceDis[matchIndex] < 0.35:

                    print("inside first if")
                    print("distance value",faceDis[matchIndex])
                    # print(f"matchIndex: {matchIndex}")

                    if matches[matchIndex]:
                        student_info = classNames[matchIndex]
                        student_id = student_info[1]
                        student_name = student_info[0]
                        y1,x2,y2,x1 = faceLoc
                        y1, x2, y2, x1 = y1*4,x2*4,y2*4,x1*4
                        cv2.rectangle(img,(x1,y1),(x2,y2),(0,255,0),2)
                        cv2.rectangle(img,(x1,y2-35),(x2,y2),(0,255,0),cv2.FILLED)
                        cv2.putText(img,student_name,(x1+6,y2-6),cv2.FONT_HERSHEY_COMPLEX,1,(255,255,255),2)
                        if student_id not in attended_student:
                            cv2.putText(img,student_name,(x1+6,y2-6),cv2.FONT_HERSHEY_COMPLEX,1,(255,255,255),2)
                            attended_student.append(student_id)
                            print(f"attendence of student {student_name} is going to happen")
                            markAttendance(student_id, col_idx)

                        else:
                            cv2.putText(img,"Present",(x1+6,y1+5),cv2.FONT_HERSHEY_COMPLEX,1,(255,255,255),2)
            cv2.imshow('Webcam',img)
            cv2.waitKey(1)

    elif instruct=="Next":
        print("inside Next section")
        cap.release()
        cv2.destroyAllWindows()
        update_col_index(subject, section)
        return
    else:
        print("inside stop section")
        cap.release()
        cv2.destroyAllWindows()
        update_col_index()
        sys.exit()


import os
join = os.path.join
import random
import numpy as np
from skimage import io
import SimpleITK as sitk

def dcm2nii(dcm_path, nii_path):
    """
    Convert dicom files to nii files
    """
    reader = sitk.ImageSeriesReader()
    dicom_names = reader.GetGDCMSeriesFileNames(dcm_path)
    reader.SetFileNames(dicom_names)
    image = reader.Execute()
    sitk.WriteImage(image, nii_path)

def mhd2nii(mhd_path, nii_path):
    """
    Convert mhd files to nii files
    """
    image = sitk.ReadImage(mhd_path)
    sitk.WriteImage(image, nii_path)

def nii2nii(nii_path, nii_gz_path):
    """
    Convert nii files to nii.gz files, which can reduce the file size
    """
    image = sitk.ReadImage(nii_path)
    sitk.WriteImage(image, nii_gz_path)

def nrrd2nii(nrrd_path, nii_path):
    """
    Convert nrrd files to nii files
    """
    image = sitk.ReadImage(nrrd_path)
    sitk.WriteImage(image, nii_path)

def jpg2png(jpg_path, png_path):
    """
    Convert jpg files to png files
    """
    image = io.imread(jpg_path)
    io.imsave(png_path, image)

def patchfy(img, mask, outpath, basename):
    """
    Patchfy the image and mask into 1024x1024 patches
    """
    image_patch_dir = join(outpath, "images")
    mask_patch_dir = join(outpath, "labels")
    os.makedirs(image_patch_dir, exist_ok=True)
    os.makedirs(mask_patch_dir, exist_ok=True)
    assert img.shape[:2] == mask.shape
    patch_height = 1024
    patch_width = 1024

    img_height, img_width = img.shape[:2]
    mask_height, mask_width = mask.shape

    if img_height % patch_height != 0:
        img = np.pad(img, ((0, patch_height - img_height % patch_height), (0, 0), (0, 0)), mode="constant")
    if img_width % patch_width != 0:
        img = np.pad(img, ((0, 0), (0, patch_width - img_width % patch_width), (0, 0)), mode="constant")
    if mask_height % patch_height != 0:
        mask = np.pad(mask, ((0, patch_height - mask_height % patch_height), (0, 0)), mode="constant")
    if mask_width % patch_width != 0:
        mask = np.pad(mask, ((0, 0), (0, patch_width - mask_width % patch_width)), mode="constant")

    assert img.shape[:2] == mask.shape
    assert img.shape[0] % patch_height == 0
    assert img.shape[1] % patch_width == 0
    assert mask.shape[0] % patch_height == 0
    assert mask.shape[1] % patch_width == 0

    height_steps = (img_height // patch_height) if img_height % patch_height == 0 else (img_height // patch_height + 1)
    width_steps = (img_width // patch_width) if img_width % patch_width == 0 else (img_width // patch_width + 1)

    for i in range(height_steps):
        for j in range(width_steps):
            img_patch = img[i * patch_height:(i + 1) * patch_height, j * patch_width:(j + 1) * patch_width, :]
            mask_patch = mask[i * patch_height:(i + 1) * patch_height, j * patch_width:(j + 1) * patch_width]
            assert img_patch.shape[:2] == mask_patch.shape
            assert img_patch.shape[0] == patch_height
            assert img_patch.shape[1] == patch_width
            print(f"img_patch.shape: {img_patch.shape}, mask_patch.shape: {mask_patch.shape}")
            img_patch_path = join(image_patch_dir, f"{basename}_{i}_{j}.png")
            mask_patch_path = join(mask_patch_dir, f"{basename}_{i}_{j}.png")
            io.imsave(img_patch_path, img_patch)
            io.imsave(mask_patch_path, mask_patch)

def rle_decode(mask_rle, img_shape):
    """
    #functions to convert encoding to mask and mask to encoding
    mask_rle: run-length as string formated (start length)
    shape: (height,width) of array to return
    Returns numpy array, 1 - mask, 0 - background
    """
    seq = mask_rle.split()
    starts = np.array(list(map(int, seq[0::2])))
    lengths = np.array(list(map(int, seq[1::2])))
    assert len(starts) == len(lengths)
    ends = starts + lengths
    img = np.zeros((np.product(img_shape),), dtype=np.uint8)
    for begin, end in zip(starts, ends):
        img[begin:end] = 255
    # https://stackoverflow.com/a/46574906/4521646
    img.shape = img_shape
    return img.T


import typing as tp

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader
from tqdm.notebook import tqdm

import dataset
from utils import Settings
from vit import ViT as SubOptimalViT

def get_vit_model() -> torch.nn.Module:
    model = SubOptimalViT(
        dim=128,
        mlp_dim=128,
        depth=12,
        heads=8,
        image_size=224,
        patch_size=32,
        num_classes=2,
        channels=3,
    ).to(Settings.device)
    return model

def get_train_loader() -> torch.utils.data.DataLoader:
    train_list = dataset.extract_dataset_globs(half=False)
    print(f"Train Data: {len(train_list)}")
    train_transforms = dataset.get_train_transforms()
    train_data = dataset.CatsDogsDataset(train_list, transform=train_transforms)
    train_loader = DataLoader(dataset=train_data, batch_size=Settings.batch_size, shuffle=True)

    return train_loader

def run_epoch(model, train_loader, criterion, optimizer) -> tp.Tuple[float, float]:
    epoch_loss, epoch_accuracy = 0, 0
    for i, (data, label) in tqdm(enumerate(train_loader), desc=f"[Train]"):
        data = data.to(Settings.device)
        label = label.to(Settings.device)
        output = model(data)
        loss = criterion(output, label)

        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        acc = (output.argmax(dim=1) == label).float().mean()
        epoch_accuracy += acc / len(train_loader)
        epoch_loss += loss / len(train_loader)
    return epoch_loss, epoch_accuracy

def main():
    model = get_vit_model()
    train_loader = get_train_loader()
    criterion = nn.CrossEntropyLoss()
    optimizer = optim.Adam(model.parameters(), lr=Settings.lr)

    run_epoch(model, train_loader, criterion, optimizer)

if __name__ == "__main__":
    main()


from typing import Iterable, List

from torchsim.core.graph.node_base import NodeBase

_node_processed = -2
_node_not_visited = -1

class IllegalCycleException(Exception):
    def __init__(self, nodes: List[NodeBase]):
        ids = [node.id for node in nodes]
        self.message = f"Illegal cycle with no backward edges detected in {ids}"

def order_nodes(nodes: Iterable[NodeBase]):
    """Calculates topological ordering of the nodes.

    The first node has an order of 1.
    """
    # We don't know where the graph starts, or what parts are connected, so we have to check for cycles from all nodes
    for node in nodes:
        _detect_illegal_cycles(node, [])

    _clear_ordering(nodes)
    destinations = _find_destinations(nodes)

    ordered_nodes = []

    for destination in destinations:
        _visit_node(destination, nodes, ordered_nodes)

    for order, node in enumerate(ordered_nodes):
        node.topological_order = order + 1

    return ordered_nodes

def _clear_ordering(nodes: Iterable[NodeBase]):
    for node in nodes:
        node.topological_order = _node_not_visited

def _visit_node(current_node: NodeBase, nodes: Iterable[NodeBase], ordered_nodes: List[NodeBase]):
    if current_node.topological_order != _node_not_visited:
        # The node was already visited.
        return

    # Mark node as processed.
    current_node.topological_order = _node_processed

    for input_slot in current_node.inputs:
        connection = input_slot.connection
        if connection is None:
            continue

        if connection.is_backward:
            continue

        source_node = connection.source.owner
        if source_node in nodes:
            _visit_node(source_node, nodes, ordered_nodes)

    ordered_nodes.append(current_node)

def _find_destinations(nodes: Iterable[NodeBase]):
    destinations = list(nodes)

    for node in nodes:
        for input_slot in node.inputs:
            connection = input_slot.connection
            if connection is None or connection.is_backward:
                continue

            source_node = connection.source.owner
            if source_node in nodes and source_node in destinations:
                destinations.remove(source_node)

    return destinations

def _detect_illegal_cycles(node: NodeBase, visited_nodes: List[NodeBase], low_priority_edge_found: bool = False):
    """Recursively explores all outgoing connections from a node looking for cycles.

    Recurses until there is no outgoing connections to explore, or until it visits a node which is has done before.
    On encountering a previously visited node, it will throw an exception none of the connections were low priority.

    WARN: This has to be initially run from each node in the topology if the topological ordering is not known.
    """
    if node in visited_nodes:
        if low_priority_edge_found:
            return
        else:
            raise IllegalCycleException(visited_nodes)

    visited_nodes.append(node)
    for block in node.outputs:
        for connection in block.connections:
            _detect_illegal_cycles(connection.target.owner, list(visited_nodes),
                                   low_priority_edge_found or connection.is_backward)


import os
import telebot
from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton
from pycoingecko import CoinGeckoAPI
from dotenv import load_dotenv

load_dotenv()
coinGecko = CoinGeckoAPI()
APITOKEN = os.environ.get("APITOKEN")

crypto_currencies = [
    "Bitcoin",
    "Ethereum",
    "Tether",
    "Cardano",
    "binancecoin",
    "Ripple",
    "Dogecoin",
    "Ethereum-Classic",
    "Polkadot",
    "USD-Coin",
    "binance-usd",
    "Bitcoin-Cash",
    "Solana",
    "Dai",
    "Litecoin",
]

myBot = telebot.TeleBot(APITOKEN)

def gen_markup():
    markup = InlineKeyboardMarkup()
    markup.row_width = 3
    for i in range(0, 14, 3):
        markup.add(
            InlineKeyboardButton(
                crypto_currencies[i], callback_data=crypto_currencies[i]
            ),
            InlineKeyboardButton(
                crypto_currencies[i + 1], callback_data=crypto_currencies[i + 1]
            ),
            InlineKeyboardButton(
                crypto_currencies[i + 2], callback_data=crypto_currencies[i + 2]
            ),
        )
    return markup

@myBot.message_handler(commands=["start", "all"])
def send_welcome(message):
    msg = myBot.send_message(
        message.chat.id,
        "Select a crypto-currency to get price",
        reply_markup=gen_markup(),
    )

@myBot.callback_query_handler(func=lambda call: True)
def callback_query(call):

    try:

        info = coinGecko.get_price(ids=call.data, vs_currencies="usd")

        myBot.send_message(
            call.from_user.id,
            call.data + " Price is\n" + "$ " + str(info[str(call.data).lower()]["usd"]),
        )

    except:
        myBot.send_message(call.from_user.id, "Something went wrong! \nPlease try again later")

myBot.infinity_polling()


import discord
from discord.ext import commands, tasks
import datetime
import character_sheet, json, asyncio
import random
import Googlify, RockPaperScissors, boons, DeityMessage, CanonList, OocMessage, hmk, valentine_generator, roll, RoleAssignment, TrickOrTreat, FishingGame
import re, time, os
import BotseriaServers
import aioscheduler

intents = discord.Intents.default()
intents.dm_reactions = True
intents.members = True

TOKEN = open('token.token').read()

bot = commands.Bot(command_prefix='>', case_insensitive=True, intents=intents, activity=discord.Game('with 0 pets today.'))
servers = BotseriaServers.PopulateServers()

bot.hmkGames = []
bot.rpsGames = []
bot.totGames = []
bot.factionMessages = BotseriaServers.PopulateFactionMessages(servers)
bot.canonMessages = {}
bot.oocMessages = {}
bot.hmkScoreMessages = {}
bot.roleMessages = {}
bot.petCounter = 0
bot.fishingGame = None
bot.monthCheck = False

def check_if_test_channel(ctx):
	#return ctx.channel.id == TESTCHANNEL
	if(str(ctx.guild.id) in servers):
		testchannels = [servers[guild]['testchannel'] for guild in servers]
		return ctx.channel.id in testchannels
	return False

def check_if_staff_or_test(ctx):
	#return ctx.guild.get_role(STAFFROLE) in ctx.author.roles or check_if_test_channel(ctx)
	if(str(ctx.guild.id) in servers):
		staffrole = servers[str(ctx.guild.id)]['staff']
		#print([servers[guild].keys() for guild in servers])
		#staffrole = [servers[guild]['staff'] for guild in servers if ctx.guild.get_role(int(servers[guild]['staff'])) != None][0]
		return ctx.guild.get_role(int(staffrole)) in ctx.author.roles or check_if_test_channel(ctx)
	return False

def check_if_december(ctx):
	return (time.localtime().tm_mon == 12) or (check_if_staff_or_test(ctx))

def check_if_february(ctx):
	return (time.localtime().tm_mon == 2) or (check_if_staff_or_test(ctx))

def check_if_october(ctx):
	return (time.localtime().tm_mon == 10) or (check_if_staff_or_test(ctx))

def check_if_bot_spam(ctx):
	if(str(ctx.guild.id) in servers):
		botchannel = int(servers[str(ctx.guild.id)]['botchannel'])
		return (ctx.channel.id == botchannel or check_if_staff_or_test(ctx))
	return False

@bot.event
async def on_ready():
	print('Logged in as')
	print(bot.user.name)
	print(bot.user.id)
	print('------')

@bot.command()
@commands.check(check_if_staff_or_test)
async def logout(ctx):
	await bot.logout()
	await bot.close()

@bot.command()
@commands.check(check_if_staff_or_test)
async def repopulate(ctx):
	'''***STAFF ONLY***'''
	await character_sheet.repopulate(ctx, servers)
	await ctx.send('Repopulated character list!')

@bot.command()
@commands.check(check_if_staff_or_test)
async def fetch(ctx):
	'''***STAFF ONLY***'''
	await character_sheet.Fetch(ctx.channel, ctx, servers)

@bot.command()
async def char(ctx, *, arg):
	name = arg.lower()
	if(str(ctx.guild.id) in servers):
		with open('servers/{}/data.json'.format(ctx.guild.id)) as json_data:
			data = json.load(json_data)
		if(name in data.keys()):
			characterInfo = data[name]
			output = character_sheet.MakeEmbed(name.title(), characterInfo, servers[str(ctx.guild.id)])
			await ctx.send(embed=output)
		else:
			output = 'Invalid character!\n```{}```'.format(name)
			await ctx.send(output)

@bot.command()
async def charlist(ctx, *, arg=''):
	if(len(arg) > 0):
		messageList = arg.split()
		if(messageList[-1].isdigit()):
			command = ' '.join(messageList[0:-1]).lower()
			number = int(messageList[-1])
		else:
			command = arg.lower()
			number = None
	else:
		command = ''
		number = None
	if(command == ''):
		# List all characters on Heavensfall
		if(number == None):
			bot.factionMessages['all'] = DeityMessage.DeityMessage('all', ctx.author, ctx, servers)
			await bot.factionMessages['all'].Send(ctx.channel)
			await bot.factionMessages['all'].ListNames()
		# Skip to a specific number of an existing message
		else:
			allMessage = bot.factionMessages['all']
			if(ctx.author == allMessage.user):
				if(number <= len(allMessage.characterList) and number >= 1):
					allMessage.index = number - 1
					await allMessage.Edit()
	# Deity character lists
	elif(command in bot.factionMessages):
		deity = command.lower()
		# List all characters of that deity
		if(number == None):
			bot.factionMessages[deity] = DeityMessage.DeityMessage(deity, ctx.author, ctx, servers)
			await bot.factionMessages[deity.lower()].Send(ctx.channel)
			await bot.factionMessages[deity].ListNames()
		# Skip to a specific number of an existing message
		else:
			factionMessage = bot.factionMessages[command]
			if(ctx.author == factionMessage.user):
				if(number <= len(factionMessage.characterList) and number >= 1):
					factionMessage.index = number-1
					await factionMessage.Edit()
	# Canon character lists
	elif(command in [canon[0] for canon in CanonList.CanonList(servers[str(ctx.guild.id)])]):
		canon = command
		# List all characters of that canon
		if(number == None):
			bot.canonMessages[canon] = CanonList.CanonMessage(canon, ctx.author, ctx, servers)
			await bot.canonMessages[canon].Send(ctx.channel)
			await bot.canonMessages[canon].ListNames()
		# Skip to a specific number of an existing message
		else:
			if(canon in bot.canonMessages):
				canonMessage = bot.canonMessages[canon]
				if(ctx.author == canonMessage.user):
					if(number <= len(canonMessage.characterList) and number >= 1):
						canonMessage.index = number - 1
						await canonMessage.Edit()
	# OOC character lists
	elif(command in OocMessage.OocList(ctx)):
		oocName = command
		# List all characters of that OOC
		if(number == None):
			bot.oocMessages[oocName] = OocMessage.OocMessage(ctx.author, oocName, ctx, servers)
			await bot.oocMessages[oocName].Send(ctx.channel)
			await bot.oocMessages[oocName].ListNames()
		# Skip to a specific number of an existing message
		else:
			oocMessage = bot.oocMessages[oocName]
			if(ctx.author == oocMessage.user):
				if(number <= len(oocMessage.characterList) and number >= 1):
					oocMessage.index = number - 1
					await oocMessage.Edit()

@bot.command()
async def canonlist(ctx):
	canonList = CanonList.CanonList(servers[str(ctx.guild.id)])
	index = 0
	sendString = 'Valid canons: ```'
	while(index < len(canonList)):
		while(len(sendString) < 1900):
			sendString += ('\n' + '{:<30}: {}'.format(canonList[index][0], canonList[index][1]))
			index += 1
			if(index >= len(canonList)):
				break
		sendString += '```'
		await ctx.send(sendString)
		sendString = '```'
	await ctx.send('>charlist [canon] to list characters for a canon')

@bot.command()
async def forward(ctx):
	await ctx.send('and back')
	await asyncio.sleep(2)
	await ctx.send('and then forward and back')
	await asyncio.sleep(2)
	await ctx.send('and then go forward and back')
	await asyncio.sleep(2)
	await ctx.send('and put one foot')
	await ctx.send('forward')

@bot.command()
@commands.check(check_if_staff_or_test)
async def rolemessage(ctx):
	#await ctx.send('Test',view=RoleAssignment.RoleAssignment())
	#embed = discord.Embed()
	#embed.description = "React to add role:\nðŸ‘© she/her\nðŸ‘¨ he/him\nðŸ‘¤ they/them"
	#bot.roleMessages[ctx.guild.id] = await ctx.send(embed=embed)
	#await bot.roleMessages[ctx.guild.id].add_reaction('ðŸ‘©')
	#await bot.roleMessages[ctx.guild.id].add_reaction('ðŸ‘¨')
	#await bot.roleMessages[ctx.guild.id].add_reaction('ðŸ‘¤')
	bot.roleMessages[ctx.guild.id] = RoleAssignment.RoleMessage(ctx)
	print(ctx.guild.id)
	await bot.roleMessages[ctx.guild.id].Send(ctx)

@bot.command()
#@commands.check(check_if_bot_spam)
async def fish(ctx):
	bot.fishingGame = FishingGame.FishingGame(ctx)
	await ctx.send("Reel in when a fish bites!", view=bot.fishingGame)
	await asyncio.sleep(5)
	await bot.fishingGame.ChangeStatus('Bite')
	if(bot.fishingGame.finished == False):
		await ctx.send("Oh! A bite!")
	await asyncio.sleep(5)
	await bot.fishingGame.ChangeStatus('Got Away')

@bot.command()
async def iam(ctx, *, arg=''):
	if(arg == ''):
		validRoles = servers[str(ctx.guild.id)]["roles"]
		#await ctx.send('I need to know what role you want, silly! Valid roles:\n```{}```'.format('\n'.join(list(ROLES.keys()))))
		await ctx.send('I need to know what role you want, silly! Valid roles:\n```{}```'.format('\n'.join(list(validRoles.keys()))))
	elif(arg.lower() == 'the bone of my sword'):
		# Kat is a weeb
		await ctx.send('Steel is my Body and Fire is my Blood.')
		await asyncio.sleep(2)
		await ctx.send('I have created over a Thousand Blades,')
		await asyncio.sleep(2)
		await ctx.send('Unknown to Death,')
		await asyncio.sleep(2)
		await ctx.send('Nor known to Life.')
		await asyncio.sleep(2)
		await ctx.send('Have withstood Pain to create many Weapons')
		await asyncio.sleep(2)
		await ctx.send('Yet those Hands will never hold Anything.')
		await asyncio.sleep(2)
		await ctx.send('So, as I Pray-')
		await asyncio.sleep(2)
		await ctx.send('**UNLIMITED BLADE WORKS**')
		await asyncio.sleep(1)
		await ctx.send(':crossed_swords:'*125)
	elif(arg.lower() == 'the night'):
		Googlify.Batmanify(Googlify.ImageFromURL(str(ctx.author.avatar))).save('tempBat.png')
		await ctx.send(file=discord.File('tempBat.png'))
	else:
		desiredRole = arg.lower()
		validRoles = servers[str(ctx.guild.id)]["roles"]
		#if(desiredRole in ROLES.keys()):
		if(desiredRole in validRoles.keys()):
			#if(ctx.guild.get_role(ROLES[desiredRole]) in ctx.author.roles):
			if(ctx.guild.get_role(validRoles[desiredRole]) in ctx.author.roles):
				await ctx.send('You already have role\n```{}```'.format(desiredRole))
			else:
				#await ctx.author.add_roles(ctx.guild.get_role(ROLES[desiredRole]))
				await ctx.author.add_roles(ctx.guild.get_role(validRoles[desiredRole]))
				await ctx.send('Role `{}` assigned!'.format(desiredRole))
		else:
			await ctx.send('Role `{}` not found.'.format(desiredRole))

@bot.command()
async def iamnot(ctx, *, arg=''):
	if(arg == ''):
		validRoles = servers[str(ctx.guild.id)]["roles"]
		#await ctx.send('I need to know what role you aren\'t, silly! Valid roles:\n```{}```'.format('\n'.join(list(ROLES.keys()))))
		await ctx.send('I need to know what role you aren\'t, silly! Valid roles:\n```{}```'.format('\n'.join(list(validRoles.keys()))))
	else:
		validRoles = servers[str(ctx.guild.id)]["roles"]
		desiredRole = arg.lower()
		if(desiredRole in validRoles.keys()):
			#if(ctx.guild.get_role(ROLES[desiredRole]) in ctx.author.roles):
			if(ctx.guild.get_role(validRoles[desiredRole]) in ctx.author.roles):
				#await ctx.author.remove_roles(ctx.guild.get_role(ROLES[desiredRole]))
				await ctx.author.remove_roles(ctx.guild.get_role(validRoles[desiredRole]))
				await ctx.send('Removed role\n```{}```'.format(desiredRole))
			else:
				await ctx.send('You do not have role\n```{}```'.format(desiredRole))
		else:
			await ctx.send('Role `{}` not found.'.format(desiredRole))

@bot.command()
async def sector(ctx):
	#faction = random.choice(FACTIONS)
	factions = servers[str(ctx.guild.id)]['factions']
	faction = random.choice(factions)
	text = ['Uplander! Uplander, make lookings! Swooshy spellycastings is sayings....um....is sayings {} is good choosymakes!'.format(faction),
			'According to the position of the stars, the placement of your bedroom, and the ripeness of this pickle - {} is the best sector for you!'.format(faction),
			'The fates have chosen, oh indecisive one - {} has found you worthy to fight in their name!'.format(faction)]
	await ctx.send(random.choice(text))

@bot.command()
async def hitme(ctx):
	with open('servers/{}/data.json'.format(ctx.guild.id)) as json_data:
		data = json.load(json_data)
	character = random.choice(list(data.keys()))
	characterInfo = data[character]
	output = character_sheet.MakeEmbed(character.title(), characterInfo, servers[str(ctx.guild.id)])
	await ctx.send(embed=output)

@bot.command()
async def templates(ctx):
	await ctx.send('You can find Magician\'s templates here!\n{}'.format(servers[str(ctx.guild.id)]["templates"]))

@bot.command()
#@commands.check(check_if_bot_spam)
async def googlify(ctx, *, arg=''):
	#print(type(ctx.author.avatar))
	if(arg == ''):
		Googlify.Googlify(Googlify.ImageFromURL(str(ctx.author.avatar))).save('tempGoogly.png')
		await ctx.send(file=discord.File('tempGoogly.png'))
	elif(len(ctx.message.mentions) > 0):
		Googlify.Googlify(Googlify.ImageFromURL(str(ctx.message.mentions[0].avatar))).save('tempGoogly.png')
		await ctx.send(file=discord.File('tempGoogly.png'))
	else:
		with open('servers/{}/data.json'.format(ctx.guild.id)) as json_data:
			data = json.load(json_data)
		if(arg.lower() in data.keys()):
			Googlify.Googlify(Googlify.ImageFromURL(data[arg.lower()]['image'])).save('tempGoogly.png')
			await ctx.send(file=discord.File('tempGoogly.png'))
		else:
			await ctx.send('Character not found:```{}```'.format(arg.lower()))

@bot.command()
@commands.check(check_if_december)
async def santafy(ctx, *, arg=''):
	if(arg == ''):
		Googlify.Santafy(Googlify.ImageFromURL(str(ctx.author.avatar))).save('tempSanta.png')
		await ctx.send(file=discord.File('tempSanta.png'))
	elif(len(ctx.message.mentions) > 0):
		Googlify.Santafy(Googlify.ImageFromURL(str(ctx.message.mentions[0].avatar))).save('tempSanta.png')
		await ctx.send(file=discord.File('tempSanta.png'))
	else:
		with open('servers/{}/data.json'.format(ctx.guild.id)) as json_data:
			data = json.load(json_data)
		if(arg.lower() in data.keys()):
			Googlify.Santafy(Googlify.ImageFromURL(data[arg.lower()]['image'])).save('tempSanta.png')
			await ctx.send(file=discord.File('tempSanta.png'))
		else:
			await ctx.send('Character not found:```{}```'.format(arg.lower()))

@bot.command()
@commands.check(check_if_october)
async def spookify(ctx, *, arg=''):
	if(arg == ''):
		Googlify.Spookify(Googlify.ImageFromURL(str(ctx.author.avatar))).save('tempSpooky.png')
		await ctx.send(file=discord.File('tempSpooky.png'))
	elif(len(ctx.message.mentions) > 0):
		Googlify.Spookify(Googlify.ImageFromURL(str(ctx.message.mentions[0].avatar))).save('tempSpooky.png')
		await ctx.send(file=discord.File('tempSpooky.png'))
	else:
		with open('servers/{}/data.json'.format(ctx.guild.id)) as json_data:
			data = json.load(json_data)
		if(arg.lower() in data.keys()):
			Googlify.Spookify(Googlify.ImageFromURL(data[arg.lower()]['image'])).save('tempSpooky.png')
			await ctx.send(file=discord.File('tempSpooky.png'))
		else:
			await ctx.send('Character not found:```{}```'.format(arg.lower()))

@bot.command()
@commands.check(check_if_december)
async def santafly(ctx, *, arg=''):
	if(arg == ''):
		Googlify.Santafy(Googlify.ImageFromURL(str(ctx.author.avatar)), rand=True).save('tempSanta.png')
		await ctx.send(file=discord.File('tempSanta.png'))
	elif(len(ctx.message.mentions) > 0):
		Googlify.Santafy(Googlify.ImageFromURL(str(ctx.message.mentions[0].avatar)), rand=True).save('tempSanta.png')
		await ctx.send(file=discord.File('tempSanta.png'))
	else:
		with open('servers/{}/data.json'.format(ctx.guild.id)) as json_data:
			data = json.load(json_data)
		if(arg.lower() in data.keys()):
			Googlify.Santafy(Googlify.ImageFromURL(data[arg.lower()]['image']), rand=True).save('tempSanta.png')
			await ctx.send(file=discord.File('tempSanta.png'))
		else:
			await ctx.send('Character not found:```{}```'.format(arg.lower()))

@bot.command(name='2019ify')
@commands.check(check_if_december)
async def Happy2019(ctx, *, arg=''):
	if(arg == ''):
		Googlify.Happy2019(Googlify.ImageFromURL(str(ctx.author.avatar))).save('temp2019.png')
		await ctx.send(file=discord.File('temp2019.png'))
	elif(len(ctx.message.mentions) > 0):
		Googlify.Happy2019(Googlify.ImageFromURL(str(ctx.message.mentions[0].avatar))).save('temp2019.png')
		await ctx.send(file=discord.File('temp2019.png'))
	else:
		with open('servers/{}/data.json'.format(ctx.guild.id)) as json_data:
			data = json.load(json_data)
		if(arg.lower() in data.keys()):
			Googlify.Happy2019(Googlify.ImageFromURL(data[arg.lower()]['image'])).save('temp2019.png')
			await ctx.send(file=discord.File('temp2019.png'))
		else:
			await ctx.send('Character not found:```{}```'.format(arg.lower()))

@bot.command()
@commands.check(check_if_february)
async def valentines(ctx, fromChar='', toChar=''):
	""">valentines "character 1" "character 2" for a cheesy MS Paint Valentine's card.
	   Character names need to be enclosed in "quotation marks" """
	if(fromChar == '' or toChar == ''):
		ctx.send("I need to know who's sending a valentines to whom, silly!")
	else:
		with open('servers/{}/data.json'.format(ctx.guild.id)) as json_data:
			data = json.load(json_data)
		if(fromChar.lower() not in data.keys()):
			await ctx.send('Character not found:```{}```'.format(fromChar.lower()))
		elif(toChar.lower() not in data.keys()):
			await ctx.send('Character not found:```{}```'.format(toChar.lower()))
		else:
			fromImage = Googlify.ImageFromURL(data[fromChar.lower()]['image'])
			toImage = Googlify.ImageFromURL(data[toChar.lower()]['image'])
			valentine_generator.MakeImage(fromImage, toImage).save('tempValentine.png')
			await ctx.send(file=discord.File('tempValentine.png'))

@bot.command()
#@commands.check(check_if_bot_spam)
async def rps(ctx):
	'''Mention someone with @<username> to challenge them to a rousing bout of rock-paper-scissors.
	   Check your DMs and respond with an emoji to play the game. The winner will be announced publicly.'''
	if(len(ctx.message.mentions) > 0):
		bot.rpsGames.sort(key=lambda x: x.valid, reverse=True)
		while(False in [game.valid for game in bot.rpsGames]):
			bot.rpsGames.pop()

		if(ctx.message.mentions[0].dm_channel == None):
			await ctx.message.mentions[0].create_dm()
		if(ctx.author.dm_channel == None):
			await ctx.author.create_dm()
		bot.rpsGames.append(RockPaperScissors.Game(ctx.message, ctx.author, ctx.message.mentions[0]))
		await bot.rpsGames[-1].Send()

@bot.command()
@commands.check(check_if_october)
async def tot(ctx):
	'''Mention someone with @<username> to challenge them to a rousing bout of trick or treat.
	   Check your DMs and respond with an emoji. The result will be announced publicly.'''
	if(len(ctx.message.mentions) > 0):
		bot.totGames.sort(key=lambda x: x.valid, reverse=True)
		while(False in [game.valid for game in bot.rpsGames]):
			bot.rpsGames.pop()
		if(ctx.message.mentions[0].dm_channel == None):
			await ctx.message.mentions[0].create_dm()
		if(ctx.author.dm_channel == None):
			await ctx.author.create_dm()
		bot.totGames.append(TrickOrTreat.Game(ctx.message, ctx.author, ctx.message.mentions[0]))
		await bot.totGames[-1].Send()

@bot.command(name='hmk')
#@commands.check(check_if_bot_spam)
async def hugmarrykill(ctx, *, arg=''):
	'''Mention someone with @<username> to begin a hug-marry-kill game.
	   Use >hmk with no arguments to show the scores for all characters.
	   Use >hmk <charname> to show the scores for a particular character.'''
	# Start a game if someone is mentioned
	if(len(ctx.message.mentions) > 0):
		# Sort games by validity, so invalid games are at the end of the list
		bot.hmkGames.sort(key = lambda x: x.valid, reverse=True)
		# Remove all invalid games. Games are invalid if they failed to start or ended.
		while(False in [game.valid for game in bot.hmkGames]):
			bot.hmkGames.pop()
		# Initialize a new game
		bot.hmkGames.append(hmk.Game(ctx.message, ctx.author, ctx.message.mentions[0], ctx.guild.id, servers[str(ctx.guild.id)]))
		await bot.hmkGames[-1].Send()
	# Search for the scores of a character
	elif(arg != ''):
		name = arg.lower()
		# Check if the server is valid
		if(str(ctx.guild.id) in servers):
			# Fetch character data
			with open('servers/{}/data.json'.format(ctx.guild.id)) as json_data:
				data = json.load(json_data)
			# Fetch hmk score data
			with open('servers/{}/hmk_scores.json'.format(ctx.guild.id)) as json_data:
				hmkdata = json.load(json_data)
			# Check if the character is valid
			if(name in data.keys()):
				characterInfo = data[name]
				# Check if the character has scores. If so, send their scores.
				if(name in hmkdata.keys()):
					output = hmk.HMKEmbed(name.title(), characterInfo, hmkdata[name], servers[str(ctx.guild.id)])
				# If the character has no hmk scores, give them 0 in each category.
				else:
					output = hmk.HMKEmbed(name.title(), characterInfo, {'hug': 0, 'marry': 0, 'kill': 0}, servers[str(ctx.guild.id)])
				await ctx.send(embed=output)
			else:
				output = 'Invalid character!\n```{}```'.format(name)
				await ctx.send(output)
	# Show all scores with an interactable embed.
	else:
		bot.hmkScoreMessages[ctx.author] = hmk.ScoreMessage(ctx.author, ctx, servers)
		await bot.hmkScoreMessages[ctx.author].Send(ctx.channel)
		await bot.hmkScoreMessages[ctx.author].ListNames()

@bot.command()
#@commands.check(check_if_bot_spam)
async def pet(ctx):
	'''Pet the bot. It is a good bot.
	Shoutouts to my friend Haruka for implementing this in a different bot, which I stole it from.'''
	responses = ['woof', 'bwoof', 'bark', 'bork', 'arf']
	bot.petCounter += 1
	await bot.change_presence(activity=discord.Game("with {} pets today.".format(bot.petCounter)))
	await ctx.send('{}'.format(random.choice(responses)))
@bot.command()
#@commands.check(check_if_bot_spam)
async def bulba(ctx):
	'''Show a v good bulba'''
	bulbaDirectory = './Bulbas/'
	bulbaFile = random.choice(os.listdir(bulbaDirectory))
	await ctx.send(file=discord.File(bulbaDirectory+bulbaFile))
#@bot.command(name='boons')
#@commands.check(check_if_staff_or_test)
#async def boonslist(ctx, number=10, minEX=1, minS=1):
#async def boonslist(ctx, *args):
#	if(len(args) > 0):
#		number= args[0]
#	minEX = 1
#	minS = 1
#	if(len(args) > 1):
#		names = args[1:]
#	batch = boons.BoonBatch(number, minEX, minS)
#	embeds = []
#	for boon in range(len(batch)):
#		if(boon < len(names)):
#			embeds.append(boons.BoonEmbed(batch[boon], names[boon]))
#		else:
#			embeds.append(boons.BoonEmbed(batch[boon]))
#	#embeds = [boons.BoonEmbed(batch[boon], names[boon]) for boon in range(len(batch))]
#	for embed in embeds:
#		await ctx.send(embed=embed)

@bot.command(name='boons')
async def boonslist(ctx, *, arg=''):
	args = arg.split('\n')
	if(len(args) > 0):
		number = len(args)
	else:
		number = 10
	minEX = 1
	minS = 1
	if(len(args) > 0):
		names = args
	else:
		names = []
	batch = boons.BoonBatch(number, minEX, minS)
	embeds = []
	for boon in range(len(batch)):
		if(boon < len(names)):
			embeds.append(boons.BoonEmbed(batch[boon], names[boon]))
		else:
			embeds.append(boons.BoonEmbed(batch[boon]))
	for embed in embeds:
		await ctx.send(embed=embed)

@bot.command(name='roll')
async def rolldie(ctx, *, arg=''):
	await ctx.send(roll.roll(arg))

@bot.command()
async def remind(ctx, *, arg=''):
	starting_time = datetime.datetime.utcnow()
	scheduler = aioscheduler.TimedScheduler()
	scheduler.start()
	scheduler.schedule(ctx.send('{}: {}'.format(ctx.author.mention, ctx.message.jump_url)), starting_time+datetime.timedelta(days=int(arg)))

@tasks.loop(hours=1)
async def RemindStartOfMonth():
	channel = bot.get_channel(880561729308868608)
	if(channel and datetime.datetime.now().day == 1 and not bot.monthCheck):
		await channel.send('{} do monthly stuff!'.format(bot.get_guild(880560673304768582).get_role(880560940905533460).mention))
		#await channel.send('do monthly stuff!')
		bot.monthCheck = True
	elif(datetime.datetime.now().day != 1):
		bot.monthCheck = False

@tasks.loop(hours=24)
async def ResetPetCounter():
	bot.petCounter = 0
	await bot.change_presence(activity=discord.Game("with {} pets today.".format(bot.petCounter)))

@ResetPetCounter.before_loop
async def before_ResetPetCounter():
	now = datetime.datetime.utcnow()
	later = now.replace(hour=4,minute=0,second=0)
	if(later < now):
		later += datetime.timedelta(days=1)
	#await asyncio.sleep_until(later)
	await discord.utils.sleep_until(later)

@bot.event
async def on_raw_reaction_add(payload):
	if(payload.member != bot.user):
		for roleMsg in bot.roleMessages:
			if(bot.roleMessages[roleMsg].message.id == payload.message_id):
				await bot.roleMessages[roleMsg].SetRole(str(payload.emoji), payload.member)
@bot.event
async def on_reaction_add(reaction, user):
	if(user != bot.user):
		for rpsGame in bot.rpsGames:
			if(rpsGame.valid):
				if(reaction.message.id == rpsGame.challengerMessage.id and user == rpsGame.challenger):
					await rpsGame.UpdateChallengerResponse(reaction)
				elif(reaction.message.id == rpsGame.targetMessage.id and user == rpsGame.target):
					await rpsGame.UpdateTargetResponse(reaction)
		for totGame in bot.totGames:
			if(totGame.valid):
				if(reaction.message.id == totGame.targetMessage.id and user == totGame.target):
					await totGame.UpdateTargetResponse(reaction)
		for hmkGame in bot.hmkGames:
			if(hmkGame.valid):
				if(reaction.message.id == hmkGame.targetMessage.id and user == hmkGame.target):
					if(str(reaction) == str(hmk.arrowLeft)):
						await hmkGame.Back()
					elif(str(reaction) == str(hmk.arrowRight)):
						await hmkGame.Advance()
					elif(str(reaction) == str(hmk.listEmoji)):
						await hmkGame.ListNames()
					else:
						await hmkGame.RegisterAnswer(reaction.emoji)
		for deity in bot.factionMessages:
			if(bot.factionMessages[deity] != None):
				if(bot.factionMessages[deity].message.id == reaction.message.id and bot.factionMessages[deity].user == user):
					if(str(reaction) == str(DeityMessage.arrowLeft)):
						await bot.factionMessages[deity].Back()
					elif(str(reaction) == str(DeityMessage.arrowRight)):
						await bot.factionMessages[deity].Advance()
					elif(str(reaction) == str(DeityMessage.listEmoji)):
						await bot.factionMessages[deity].ListNames()
		for canon in bot.canonMessages:
			if(bot.canonMessages[canon] != None):
				if(bot.canonMessages[canon].message.id == reaction.message.id and bot.canonMessages[canon].user == user):
					if(str(reaction) == str(CanonList.arrowLeft)):
						await bot.canonMessages[canon].Back()
					elif(str(reaction) == str(CanonList.arrowRight)):
						await bot.canonMessages[canon].Advance()
					elif(str(reaction) == str(CanonList.listEmoji)):
						await bot.canonMessages[canon].ListNames()
		for ooc in bot.oocMessages:
			if(bot.oocMessages[ooc] != None):
				if(bot.oocMessages[ooc].message.id == reaction.message.id and bot.oocMessages[ooc].user == user):
					if(str(reaction) == str(OocMessage.arrowLeft)):
						await bot.oocMessages[ooc].Back()
					elif(str(reaction) == str(OocMessage.arrowRight)):
						await bot.oocMessages[ooc].Advance()
					elif(str(reaction) == str(OocMessage.listEmoji)):
						await bot.oocMessages[ooc].ListNames()
		for hmkScore in bot.hmkScoreMessages:
			if(bot.hmkScoreMessages[hmkScore] != None):
				if(bot.hmkScoreMessages[hmkScore].message.id == reaction.message.id and bot.hmkScoreMessages[hmkScore].user == user):
					if(str(reaction) == str(hmk.arrowLeft)):
						await bot.hmkScoreMessages[hmkScore].Back()
					if(str(reaction) == str(hmk.arrowRight)):
						await bot.hmkScoreMessages[hmkScore].Advance()
					if(str(reaction) == str(hmk.listEmoji)):
						await bot.hmkScoreMessages[hmkScore].ListNames()
					if(str(reaction) == str(hmk.hEmoji)):
						await bot.hmkScoreMessages[hmkScore].Sort(str(reaction))
					if(str(reaction) == str(hmk.mEmoji)):
						await bot.hmkScoreMessages[hmkScore].Sort(str(reaction))
					if(str(reaction) == str(hmk.kEmoji)):
						await bot.hmkScoreMessages[hmkScore].Sort(str(reaction))
		#for roleMsg in bot.roleMessages:
		#	if(bot.roleMessages[roleMsg].message.id == reaction.message.id):
		#		await bot.roleMessages[roleMsg].SetRole(str(reaction), user)

if(__name__ == '__main__'):
	ResetPetCounter.start()
	RemindStartOfMonth.start()
	bot.run(TOKEN)


from __future__ import print_function

import os
import unittest
import logging

from setuptools import setup
from setuptools.command.install import install
from setuptools.command.develop import develop

logging.basicConfig(level=logging.DEBUG)

PKG_DIR = os.path.abspath(os.path.dirname(__file__))
PKG_NAME = 'py_sep_sdk'
PKG_VERSION = '1.0.8'
PKG_AUTHOR_NAME = 'Paul Greenberg'
PKG_AUTHOR_HANDLE = 'greenpau'
PKG_AUTHOR_EMAIL = 'greenpau@outlook.com'
PKG_LICENSE = 'License :: OSI Approved :: Apache Software License'
PKG_DESCRIPTION = 'Symantec Endpoint Protection Manager API Client Library'
PKG_URL = 'https://github.com/%s/%s' % (PKG_AUTHOR_HANDLE, PKG_NAME)
PKG_DOWNLOAD_URL = 'https://github.com/%s/%s/archive/master.zip' % (PKG_AUTHOR_HANDLE, PKG_NAME)
PKG_LONG_DESCRIPTION = PKG_DESCRIPTION
with open(os.path.join(PKG_DIR, PKG_NAME, 'README.rst')) as f:
    PKG_LONG_DESCRIPTION = f.read()
PKG_PACKAGES = [PKG_NAME]
PKG_REQUIRES = [
    'setuptools',
    'wheel',
    'requests>=2.21.0',
    'ipaddress',
]
PKG_TEST_SUITE = 'setup._load_test_suite'
PKG_DATA = [
    'tests/test_requests.py',
    'README.rst',
    'LICENSE.txt',
    'VERSION',
]
PKG_SCRIPTS = [
    'scripts/symc-sep-client'
]
PKG_KEYWORDS = [
    'symantec',
    'sep',
    'api',
    'sdk',
    'endpoint protection',
    'symc',
]
PKG_PLATFORMS = 'any'
PKG_CLASSIFIERS = [
    'Development Status :: 5 - Production/Stable',
    'Environment :: Console',
    'Intended Audience :: Information Technology',
    'Intended Audience :: System Administrators',
    PKG_LICENSE,
    'Programming Language :: Python',
    'Operating System :: POSIX :: Linux',
    'Topic :: Utilities',
    'Topic :: System :: Systems Administration',
]

def _load_test_suite():
    test_loader = unittest.TestLoader()
    test_suite = test_loader.discover(os.path.join(PKG_DIR, PKG_NAME, 'tests'),
                                      pattern='test_requests.py')
    return test_suite

def pre_test_package():
    ''' This function runs pre-installation tasks. '''
    logging.debug('running tests ...')
    for test_suite in _load_test_suite():
        test_suite_runner = unittest.TextTestRunner()
        test_runner = test_suite_runner.run(test_suite)
        if not test_runner.failures:
            continue
        for test_failure in test_runner.failures:
            logging.error('%s', test_failure[1])
            return True
    return False

class InstallPackage(install):
    ''' This function runs installation tasks. '''
    def run(self):
        logging.debug('running install ...')
        err = pre_test_package()
        if err:
            return 1
        install.run(self)
        return 0

class UninstallPackage(develop):
    ''' This function runs uninstallation tasks. '''
    def run(self):
        logging.debug('running uninstall ...')
        develop.run(self)

CMD_CLASS = {
    'install': InstallPackage,
    'bdist_wheel': InstallPackage,
    'uninstall': UninstallPackage,
}

setup(
    name=PKG_NAME,
    version=PKG_VERSION,
    description=PKG_DESCRIPTION,
    long_description=PKG_LONG_DESCRIPTION,
    url=PKG_URL,
    download_url=PKG_DOWNLOAD_URL,
    author=PKG_AUTHOR_NAME,
    author_email=PKG_AUTHOR_EMAIL,
    license=PKG_LICENSE,
    platforms=PKG_PLATFORMS,
    classifiers=PKG_CLASSIFIERS,
    packages=PKG_PACKAGES,
    package_data={
        '': PKG_DATA,
    },
    scripts=PKG_SCRIPTS,
    keywords=PKG_KEYWORDS,
    install_requires=PKG_REQUIRES,
    test_suite=PKG_TEST_SUITE,
    cmdclass=CMD_CLASS
)


#!/usr/bin/env python
import constants as c
from buttons import Buttons
from media_group import media_group
import expited_chats_checker
from database_connection import DatabaseConnection

import json
import datetime
import random
from asyncio import sleep
import os

from aiogram import Bot, Dispatcher, executor, types, utils
from aiogram.dispatcher import FSMContext
from aiogram.dispatcher.filters.state import State, StatesGroup
from aiogram.contrib.fsm_storage.memory import MemoryStorage

WEBHOOK = True
WEBHOOK_HOST = c.app  # heroku app url
WEBHOOK_PATH = '/webhook/'
WEBHOOK_URL = f"{WEBHOOK_HOST}{WEBHOOK_PATH}"

WEBAPP_HOST = '0.0.0.0'
WEBAPP_PORT = os.environ.get('PORT')

bot = Bot(c.token)
storage = MemoryStorage()
dp = Dispatcher(bot, storage=storage)

class Activate(StatesGroup): code = State()
class Export(StatesGroup): code = State()
class Delete(StatesGroup): code = State()
class Settings(StatesGroup): option = State()

class Chat(StatesGroup):
    code = State()
    send = State()

class NewOrder(StatesGroup):
    contact = State()
    subject = State()
    level = State()
    q_type = State()  # short / multiple choice
    m_choice = State()
    s_answers = State()
    timed = State()
    duration = State()
    timezone_city = State()
    additions = State()
    accepting = State()

@dp.message_handler(commands=['start'], state=Chat)
async def message_handler(message: types.Message, state: FSMContext):
    await state.finish()
    await start_menu(message)

@dp.message_handler(commands=['start'], state=Export)
async def message_handler(message: types.Message, state: FSMContext):
    await state.finish()
    await start_menu(message)

@dp.message_handler(commands=['start'])
async def message_handler(message: types.Message):
    if message.chat.id == c.moderator_chat:
        await message.answer("Moderator mode", reply_markup=moderator_keyboard())
    else:
        await start_menu(message)

def client_keyboard() -> types.ReplyKeyboardMarkup:
    key = types.ReplyKeyboardMarkup(resize_keyboard=True)
    key.add(Buttons.my_chats_client, Buttons.new_order)
    key.add(Buttons.support)
    return key

def moderator_keyboard() -> types.ReplyKeyboardMarkup:
    key = types.ReplyKeyboardMarkup(resize_keyboard=True)
    key.add(Buttons.m_activate, Buttons.m_delete)
    key.add(Buttons.m_export, Buttons.m_show)
    return key

async def start_menu(message: types.Message):
    key = types.ReplyKeyboardMarkup(resize_keyboard=True)
    key.add(Buttons.introduction_client)
    key.add(Buttons.introduction_tutor)
    await message.answer("Just a heads up, if the buttons disappear you might want to press the button on the right "
                         "side of your keyboard. Make sure that nothing is typed in.", reply_markup=key)
    await bot.send_animation(message.chat.id, c.instruction_gif)

async def _send_message(func, **kwargs):
    try:
        await func(**kwargs)
    except utils.exceptions.BotBlocked: return
    except utils.exceptions.UserDeactivated: return
    except utils.exceptions.ChatNotFound: return
    except utils.exceptions.BadRequest: return 0
    return True

async def _back_client(message, state):
    if message.text == Buttons.back:
        await state.finish()
        await client_main(message)
        return True
    return

def create_new_code():
    color = random.choice(c.random_colors)
    word = random.choice(c.random_words)
    suffix = random.randint(0, 99)
    return f'{color}{word}{suffix}'

def save_data(chat: int, name: str, text: str = None, data_type: str = None, data: str = None):
    insertTextQuery = "INSERT INTO text_messages (chat, user, text) VALUES (%s, %s, %s)"
    insertMediaQuery = "INSERT INTO media_messages (chat, type, file_id) VALUES (%s, %s, %s)"
    if text:
        with DatabaseConnection() as db:
            conn, cursor = db
            cursor.executemany(insertTextQuery, [(chat, name, text)])
            conn.commit()
    if data:
        with DatabaseConnection() as db:
            conn, cursor = db
            cursor.executemany(insertMediaQuery, [(chat, data_type, data)])
            conn.commit()

def _media_group_builder(data, caption=True):
    media = types.MediaGroup()
    first = True
    for photo in data['media']['photo']:
        if first:
            first = False
            if caption:
                media.attach_photo(photo, data['media']['caption'])
            else:
                media.attach_photo(photo)
        else:
            media.attach_photo(photo)
    for video in data['media']['video']:
        if first:
            first = False
            if caption:
                media.attach_photo(video, data['media']['caption'])
            else:
                media.attach_photo(video)
        else:
            media.attach_video(video)
    return media

def get_price(data) -> float:
    with open('prices.json', 'r') as f:
        prices = json.load(f)

    price: float = 0
    level: str = data['level']
    m_choice: int = data.get('m_choice', 0)
    s_answers: int = data.get('s_answers', 0)
    duration_inc: int = prices['duration_inc'] if data.get('duration') is not None else 0
    questions = m_choice + s_answers
    if questions > 300:
        discount = 0.75
    elif questions > 250:
        discount = 0.71
    elif questions > 200:
        discount = 0.7
    elif questions > 150:
        discount = 0.6
    elif questions > 100:
        discount = 0.5
    elif questions > 50:
        discount = 0.4
    elif questions > 40:
        discount = 0.35
    elif questions > 30:
        discount = 0.25
    elif questions > 20:
        discount = 0.2
    elif questions > 10:
        discount = 0.1
    else:
        discount = 0

    if level == Buttons.level[0]:
        level_multiplier = prices['level_multiplier_1']
    elif level == Buttons.level[1]:
        level_multiplier = prices['level_multiplier_2']
    elif level == Buttons.level[2]:
        level_multiplier = prices['level_multiplier_3']
    else:
        level_multiplier = prices['level_multiplier_4']

    price += m_choice * prices['m_choice']
    price += s_answers * prices['s_answers']
    price *= level_multiplier
    price -= price * discount
    price += duration_inc

    return price.__round__(2)

async def tutor_main(message):
    key = types.ReplyKeyboardMarkup(resize_keyboard=True)
    key.add(Buttons.my_chats_tutor)
    key.add(Buttons.support)
    await message.answer("Choose action", reply_markup=key)

async def client_main(message):
    await message.answer("First of all, relax, because we have your back 🤓!\n\n"
                         "If you already created an order, click on “MyChats”.\n\n"
                         "If you want to create a new order, click on “New Order”.\n\n"
                         "Didn't receive an order ID? No worries! Contact our support team and we’ll sort things out.",
                         reply_markup=client_keyboard())

async def my_chats(message, who_am_i, state):
    selectQuery = "SELECT ID, code FROM chats WHERE {}=(%s)"
    with DatabaseConnection() as db:
        conn, cursor = db
        cursor.execute(selectQuery.format(who_am_i), [message.chat.id])
        chats = cursor.fetchall()
    if not chats:
        await message.answer("You haven't any chats")
        return

    key = types.ReplyKeyboardMarkup(resize_keyboard=True)
    key.add(Buttons.back)
    inline_key = types.InlineKeyboardMarkup()
    for code in chats:
        inline_key.add(types.InlineKeyboardButton(code[1], callback_data=code[0]))

    await Chat.code.set()
    await state.update_data({'who_am_i': who_am_i})
    await message.answer("There are all your chats", reply_markup=inline_key)
    await message.answer("Choose the chat", reply_markup=key)

async def chat_answer(callback_query, state):
    who_am_i = 'tutor' if callback_query.data[6:7] == 'c' else 'client'
    chat_id = callback_query.data[7:]
    key = types.ReplyKeyboardMarkup(resize_keyboard=True)
    key.add(Buttons.back)
    await Chat.send.set()
    await state.update_data({'chat_id': chat_id, 'who_am_i': who_am_i})
    await callback_query.answer()
    await callback_query.message.answer("Now all messages will be send to this interlocutor", reply_markup=key)

async def new_order_start(message):
    key = types.ReplyKeyboardMarkup(resize_keyboard=True)
    key.add(types.KeyboardButton("Share contact", request_contact=True))
    await NewOrder.contact.set()
    await message.answer("Please share your contact details with us. This allows our support team to be more efficient "
                         "in helping you. Don't worry your information is only visible to our support specialists.",
                         reply_markup=key)

async def generate_new_chat(callback_query):
    await callback_query.message.edit_reply_markup()
    text = callback_query.data[3:].split('_')
    client = int(text[0])
    price = float(text[1])
    code = create_new_code()

    insetQuery = "INSERT INTO chats (code, client, tutor) VALUES (%s, %s, %s)"
    with DatabaseConnection() as db:
        conn, cursor = db
        cursor.executemany(insetQuery, [(code, client, callback_query.from_user.id)])
        conn.commit()

    with open('prices.json', 'r') as f:
        prices = json.load(f)
    price_tutor = (price * prices['tutor_share']).__round__(2)

    key = types.InlineKeyboardMarkup()
    key.add(types.InlineKeyboardButton("Pay", url=c.paypal))
    await _send_message(bot.send_message, chat_id=client,
                        text=f"Hey! One of our tutors accepted your order. Your chat ID is: `{code}`\n"
                             f"Please deposit $*{price}* to the following PayPal account. "
                             "Don't forget to add a note with your order id to your payment.\n"
                             "Don't worry you are protected by our money back guarantee as well as PayPals buyer "
                             "protection policy. Your tutor will not get the money until your order is complete.\n\n"
                             "Your chat ID is active but will be deactivated in 30 min if the payment is not made."
                             "Thanks!\n\nTo access your chat click on 'my chats' and then your order ID.",
                        reply_markup=key, parse_mode='Markdown')
    await _send_message(bot.send_message, chat_id=callback_query.from_user.id,
                        text=f"Hey! Your chat ID is: `{code}`\nThe client has 30 min to make a deposit. "
                             f"You will be notified.", parse_mode='Markdown')
    await callback_query.answer("See your private messages with the bot", show_alert=True)

    await bot.send_message(c.moderator_chat, f"*New order!*\n*Chat ID:* `{code}`\n"
                                             f"*Price:* ${price}\n*Tutor share:* ${price_tutor}\n"
                                             f"[Client](tg://user?id={client}) "
                                             f"[Tutor](tg://user?id={callback_query.from_user.id})",
                           parse_mode='Markdown')

async def activate_chat_init(message: types.Message):
    if message.chat.id not in (c.moderator_chat, c.admin_id):
        return
    key = types.ReplyKeyboardMarkup(resize_keyboard=True)
    key.add(Buttons.back)
    await Activate.code.set()
    await message.answer("Send me the chat ID to activate", reply_markup=key)

async def export_chat_init(message: types.Message):
    if message.chat.id not in (c.moderator_chat, c.admin_id):
        return
    key = types.ReplyKeyboardMarkup(resize_keyboard=True)
    key.add(Buttons.back)
    await Export.code.set()
    await message.answer("Send me the chat ID to export", reply_markup=key)

async def delete_chat_init(message: types.Message):
    if message.chat.id not in (c.moderator_chat, c.admin_id):
        return
    key = types.ReplyKeyboardMarkup(resize_keyboard=True)
    key.add(Buttons.back)
    await Delete.code.set()
    await message.answer("Send me the chat ID to delete", reply_markup=key)

async def show_all_chats_init(message: types.Message):
    if message.chat.id not in (c.moderator_chat, c.admin_id):
        return

    selectQuery = "SELECT code, created FROM chats"
    with DatabaseConnection() as db:
        conn, cursor = db
        cursor.execute(selectQuery)
        chats = cursor.fetchall()

    buffer = ''
    for chat in chats:
        buffer += chat[0] + datetime.datetime.strftime(chat[1], "\t- %d.%m / %H:%M\n")
    with open('chats.txt', 'w') as f:
        f.write(buffer)
    await bot.send_document(message.chat.id, types.InputFile('chats.txt'), 'chats.txt')

@dp.message_handler(commands=['settings'])
async def message_handler(message: types.Message):
    if message.chat.id != c.admin_id:
        return
    with open('prices.json', 'r') as f:
        prices = json.load(f)
    buffer = ''
    for option in prices:
        buffer += f'`{option}` - {prices[option]}\n'
    key = types.ReplyKeyboardMarkup(resize_keyboard=True)
    key.add(Buttons.back)
    await Settings.option.set()
    await message.answer(buffer + "Send me the option which you want to change with the new value separated by space",
                         parse_mode='Markdown', reply_markup=key)

async def support_send(message):
    await _send_message(bot.send_message, chat_id=c.moderator_chat,
                        text=f"❗ Request for help ❗\n[User](tg://user?id={message.chat.id})", parse_mode='Markdown')
    await message.answer("Thanks for reaching out!\nOne of our support team members will get in touch with you as soon as possible.")

@dp.message_handler(content_types=['text'])
async def message_handler(message: types.Message, state: FSMContext):
    if message.text == Buttons.back:
        await start_menu(message)
    elif message.text == Buttons.introduction_tutor:
        await tutor_main(message)
    elif message.text == Buttons.introduction_client:
        await client_main(message)
    elif message.text == Buttons.my_chats_client:
        await my_chats(message, 'client', state)
    elif message.text == Buttons.my_chats_tutor:
        await my_chats(message, 'tutor', state)
    elif message.text == Buttons.new_order:
        await new_order_start(message)
    elif message.text == Buttons.support:
        await support_send(message)

    elif message.text == Buttons.m_activate:
        if message.chat.id == c.moderator_chat:
            await activate_chat_init(message)
    elif message.text == Buttons.m_delete:
        if message.chat.id == c.moderator_chat:
            await delete_chat_init(message)
    elif message.text == Buttons.m_export:
        if message.chat.id == c.moderator_chat:
            await export_chat_init(message)
    elif message.text == Buttons.m_show:
        if message.chat.id == c.moderator_chat:
            await show_all_chats_init(message)

@dp.message_handler(content_types=['text'], state=Activate.code)
async def message_handler(message: types.Message, state: FSMContext):
    await state.finish()
    if message.text == Buttons.back:
        await message.answer("Canceled", reply_markup=moderator_keyboard())
        return
    code: str = message.text.lower()

    active = None
    selectQuery = "SELECT active, client, tutor FROM chats WHERE code=(%s)"
    updateQuery = "UPDATE chats SET active=1 WHERE code=(%s)"
    with DatabaseConnection() as db:
        conn, cursor = db
        cursor.execute(selectQuery, [code])
        chat = cursor.fetchone()
        if chat:
            if chat[0] == 1:
                active = True
            else:
                active = False
                cursor.execute(updateQuery, [code])
                conn.commit()

    if active is None:
        await message.answer("Chat ID does not exist!", reply_markup=moderator_keyboard())
        return
    elif active:
        await message.answer("Chat ID is already activated!", reply_markup=moderator_keyboard())
        return

    client, tutor = chat[1], chat[2]
    await _send_message(bot.send_message, chat_id=client, text="We have received the payment, the chat is now fully active!")
    await _send_message(bot.send_message, chat_id=tutor, text="We have received the payment, the chat is now fully active!")
    await message.answer("Activated", reply_markup=moderator_keyboard())

@dp.message_handler(content_types=['text'], state=Export.code)
async def message_handler(message: types.Message, state: FSMContext):
    await state.finish()
    if message.text == Buttons.back:
        await message.answer("Canceled", reply_markup=moderator_keyboard())
        return
    code: str = message.text.lower()

    selectChatQuery = "SELECT ID FROM chats WHERE code=(%s)"
    selectMediaQuery = "SELECT type, file_id FROM media_messages WHERE chat=(%s)"
    selectTextQuery = "SELECT user, time, text FROM text_messages WHERE chat=(%s)"
    text, media = None, None
    with DatabaseConnection() as db:
        conn, cursor = db
        cursor.execute(selectChatQuery, [code])
        chat_id = cursor.fetchone()
        if chat_id:
            cursor.execute(selectMediaQuery, [chat_id[0]])
            media = cursor.fetchall()
            cursor.execute(selectTextQuery, [chat_id[0]])
            text = cursor.fetchall()

    if not chat_id:
        await message.answer("Chat does not exist!", reply_markup=moderator_keyboard())
        return
    if not text:
        await message.answer("There are no text messages")
    else:
        buffer = ''
        for t in text:
            user, time, text = t[0], t[1], t[2]
            buffer += f'{datetime.datetime.strftime(time, "%d.%m / %H:%M")} - {user}:\n{text}\n\n'
        with open(code, 'wt', encoding='utf-8') as f:
            f.write(buffer)
        del buffer
        await bot.send_document(message.chat.id, types.InputFile(code, f'{code} text messages.txt'))
        try: os.remove(code)
        except Exception as e: print(e)

    if not media:
        await message.answer("There are no media messages")
    else:
        for m in media:
            filetype, fileid = m[0], m[1]
            try:
                if filetype == c.PHOTO:
                    await bot.send_photo(message.chat.id, fileid)
                elif filetype == c.VIDEO:
                    await bot.send_video(message.chat.id, fileid)
                elif filetype == c.DOCUMENT:
                    await bot.send_document(message.chat.id, fileid)
            except Exception as e:
                await message.answer(str(e))
            await sleep(.05)
    await message.answer("Sending finished", reply_markup=moderator_keyboard())

@dp.message_handler(content_types=['text'], state=Delete.code)
async def message_handler(message: types.Message, state: FSMContext):
    await state.finish()
    if message.text == Buttons.back:
        await message.answer("Canceled", reply_markup=moderator_keyboard())
        return
    code: str = message.text.lower()
    deleteQuery = "DELETE FROM chats WHERE code=(%s)"
    with DatabaseConnection() as db:
        conn, cursor = db
        cursor.execute(deleteQuery, [code])
        conn.commit()
    await message.answer("Chat deleted", reply_markup=moderator_keyboard())

@dp.message_handler(content_types=['text'], state=Settings.option)
async def message_handler(message: types.Message, state: FSMContext):
    if message.text == Buttons.back:
        await state.finish()
        await message.answer("Canceled", reply_markup=client_keyboard())
        return
    text = message.text.split(' ')
    if len(text) != 2:
        await message.answer("Invalid input, please try again", reply_markup=client_keyboard())
        return
    option = text[0]
    value = text[1]
    try:
        value = float(value)
    except ValueError:
        await message.answer("Value is not digit, please try again", reply_markup=client_keyboard())
        return
    await state.finish()
    with open('prices.json', 'r') as f:
        prices = json.load(f)
    prices[option] = value
    with open('prices.json', 'w', encoding='utf-8') as f:
        json.dump(prices, f, indent=2)
    await message.answer("Successfully changed", reply_markup=client_keyboard())

@dp.message_handler(content_types=['text'], state=Chat.code)
async def message_handler(message: types.Message, state: FSMContext):
    if message.text == Buttons.back:
        data = await state.get_data()
        await state.finish()
        if data['who_am_i'] == 'client':
            await client_main(message)
        else:
            await tutor_main(message)

@dp.callback_query_handler(lambda callback_query: True, state=Chat.code)
async def callback_inline(callback_query: types.CallbackQuery, state: FSMContext):
    chat_id = callback_query.data
    existsQuery = "SELECT EXISTS (SELECT ID FROM chats WHERE ID=(%s))"
    with DatabaseConnection() as db:
        conn, cursor = db
        cursor.execute(existsQuery, [chat_id])
        exists = cursor.fetchone()[0]

    if not exists:
        await state.finish()
        await callback_query.answer("ID does not exist!", show_alert=True)
        await client_main(callback_query.message)
        return
    await state.update_data({'chat_id': chat_id})
    await Chat.send.set()
    await callback_query.answer()
    await callback_query.message.answer("Now all messages will be send to this interlocutor")

# SECTION: MESSAGE SEND
@dp.message_handler(content_types=['text', 'photo', 'video', 'document'], state=Chat.send)
async def message_handler(message: types.Message, state: FSMContext):
    data = await state.get_data()
    who_am_i = data.get('who_am_i')
    if message.text == Buttons.back or message.text == '/start' or not who_am_i:
        await state.finish()
        if who_am_i == 'client':
            await client_main(message)
        else:
            await tutor_main(message)
        return
    chat_id = data.get('chat_id')

    selectQuery = "SELECT client, tutor, code FROM chats WHERE ID=(%s)"
    with DatabaseConnection() as db:
        conn, cursor = db
        cursor.execute(selectQuery, [chat_id])
        chat = cursor.fetchone()

    if not chat:
        await message.answer("Chat does not exist or has expired!")
        await state.finish()
        if who_am_i == 'client':
            await client_main(message)
        else:
            await tutor_main(message)
        return
    if who_am_i == 'client':
        ilr = 1
    else:
        ilr = 0
    interlocutor = chat[ilr]
    code = chat[2]
    text = ''
    send = None
    key = None
    name = f'@{message.chat.username}' if message.chat.username else str(message.chat.id)
    state_interlocutor = await state.storage.get_data(chat=interlocutor)
    interlocutor_in_chat = state_interlocutor.get('chat_id')

    if interlocutor_in_chat != chat_id:
        key = types.InlineKeyboardMarkup()
        key.add(types.InlineKeyboardButton("Answer", callback_data=f"answer{data['who_am_i'][0]}{chat_id}"))
    if message.media_group_id:
        data = await media_group(message, state)
        if data is None: return
        await state.update_data({'media_group': True})
        await state.update_data({'media': {'photo': data['media_group']['photo'],
                                           'video': data['media_group']['video'],
                                           'caption': data['media_group']['caption']}})
        data = await state.get_data()
        photo = data['media']['photo']
        video = data['media']['video']
        text = data['media']['caption']

        media = _media_group_builder(data, caption=True)
        send = await _send_message(bot.send_media_group, chat_id=interlocutor, media=media)

        for p in photo:
            save_data(chat_id, name, text, c.PHOTO, p)
        for v in video:
            save_data(chat_id, name, text, c.VIDEO, v)
    elif message.text:
        text = message.text
        send = await _send_message(bot.send_message, chat_id=interlocutor, text=f'{code}:\n{text}', reply_markup=key)
        save_data(chat_id, name, text)
    elif message.photo:
        photo = message.photo[-1].file_id
        if message.caption:
            text = message.caption
        send = await _send_message(bot.send_photo, chat_id=interlocutor, photo=photo, caption=f'{code}:\n{text}', reply_markup=key)
        save_data(chat_id, name, text, c.PHOTO, photo)
    elif message.video:
        video = message.video.file_id
        if message.caption:
            text = message.caption
        send = await _send_message(bot.send_video, chat_id=interlocutor, video=video, caption=f'{code}:\n{text}', reply_markup=key)
        save_data(chat_id, name, text, c.VIDEO, video)
    elif message.document:
        file = message.document.file_id
        send = await _send_message(bot.send_document, chat_id=interlocutor, document=file, caption=f'{code}:\n{text}', reply_markup=key)
        save_data(chat_id, name, text, c.DOCUMENT, file)
    if send:
        await message.answer(f"Message sent to the chat `{code}`", parse_mode='Markdown')
    elif send == 0:
        await message.answer("An error occurred. The message text may be too long.")
    else:
        await message.answer("The message wasn't delivered, the user may have blocked the bot")

# New order zone
@dp.message_handler(content_types=['contact'], state=NewOrder.contact)
async def message_handler(message: types.Message, state: FSMContext):
    country_code = str(message.contact.phone_number)[:-9]
    if country_code[0] == '+': country_code = country_code[1:]
    if country_code in c.forbidden_country_codes:
        await state.finish()
        await message.answer("Sorry, your country is not supported")
        await client_main(message)
        return
    key = types.ReplyKeyboardMarkup(resize_keyboard=True)
    key.add(Buttons.subjects[0], Buttons.subjects[1], Buttons.subjects[2], Buttons.subjects[3], Buttons.subjects[4])
    key.add(Buttons.back)
    await NewOrder.subject.set()
    await message.answer("Please select the subject you need help with:\n"
                         "Math, Stats, Finance, Business, Chem\n\nThanks!", reply_markup=key)

@dp.message_handler(content_types=['text'], state=NewOrder.subject)
async def message_handler(message: types.Message, state: FSMContext):
    if await _back_client(message, state):
        return
    if message.text not in Buttons.subjects:
        return
    key = types.ReplyKeyboardMarkup(resize_keyboard=True)
    key.add(Buttons.level[0], Buttons.level[1], Buttons.level[2])
    key.add(Buttons.back)
    await state.update_data({'subject': message.text})
    await NewOrder.level.set()
    await message.answer("Next, we need to know difficulty level of your assignment. "
                         "If you consider your course to be more or less difficult than "
                         "what our level suggestions are select feel free to select level "
                         "you think best fits your course. However, its crucial that you select "
                         "appropriate difficulty level:\n\nLevel 1: High School\nLevel 2: Undergraduate "
                         "(University Year 1-2) AND IB AP courses.\nLevel 3 Undergraduate (Year 3-4)",
                         reply_markup=key)

@dp.message_handler(content_types=['text'], state=NewOrder.level)
async def message_handler(message: types.Message, state: FSMContext):
    if await _back_client(message, state):
        return
    if message.text not in Buttons.level:
        return
    key = types.ReplyKeyboardMarkup(resize_keyboard=True)
    key.add(Buttons.q_type[0])
    key.add(Buttons.q_type[1])
    key.add(Buttons.q_type[2])
    key.add(Buttons.back)
    await state.update_data({'level': message.text})
    await NewOrder.q_type.set()
    await message.answer("Next, please tell us what type of questions your assignment has? Please note that we can "
                         "only help with short answers and multiple choice questions.", reply_markup=key)

@dp.message_handler(content_types=['text'], state=NewOrder.q_type)
async def message_handler(message: types.Message, state: FSMContext):
    if await _back_client(message, state):
        return
    if message.text not in Buttons.q_type:
        return
    await state.update_data({'q_type': message.text})
    if message.text == Buttons.q_type[0]:
        await NewOrder.s_answers.set()
        answer = "Ok, how many short answer question does your assignment have? (select 0 if none)"
    else:
        await NewOrder.m_choice.set()
        answer = "How many multiple choice questions does your assignment have? (select 0 if none)"
    key = types.ReplyKeyboardMarkup(resize_keyboard=True)
    key.add(Buttons.back)
    await message.answer(answer, reply_markup=key)

@dp.message_handler(content_types=['text'], state=NewOrder.m_choice)
async def message_handler(message: types.Message, state: FSMContext):
    if await _back_client(message, state):
        return
    if not str(message.text).isdigit():
        return
    data = await state.get_data()
    await state.update_data({'m_choice': int(message.text)})
    if data.get('q_type') == Buttons.q_type[2]:
        await NewOrder.s_answers.set()
        await message.answer("Ok, how many short answer question does your assignment have? (select 0 if none)")
    else:
        if int(message.text) == 0:
            await state.finish()
            await message.answer("So, your assignment doesn't have any questions?")
            return
        else:
            key = types.ReplyKeyboardMarkup(resize_keyboard=True)
            key.add(Buttons.timed[0], Buttons.timed[1])
            key.add(Buttons.back)
            await NewOrder.timed.set()
            await message.answer("Ok, we are almost done!\n\nIs your assignment timed?", reply_markup=key)

@dp.message_handler(content_types=['text'], state=NewOrder.s_answers)
async def message_handler(message: types.Message, state: FSMContext):
    if await _back_client(message, state):
        return
    if not str(message.text).isdigit():
        return
    data = await state.get_data()
    if data.get('q_type') == Buttons.q_type[2]:
        if int(message.text) == 0 and data.get('m_choice') == 0:
            await state.finish()
            await message.answer("So, your assignment doesn't have any questions?")
            return
    key = types.ReplyKeyboardMarkup(resize_keyboard=True)
    key.add(Buttons.timed[0], Buttons.timed[1])
    key.add(Buttons.back)
    await state.update_data({'s_answers': int(message.text)})
    await NewOrder.timed.set()
    await message.answer("Ok, we are almost done!\n\nIs your assignment timed?", reply_markup=key)

@dp.message_handler(content_types=['text'], state=NewOrder.timed)
async def message_handler(message: types.Message, state: FSMContext):
    if await _back_client(message, state):
        return
    if message.text not in Buttons.timed:
        return
    key = types.ReplyKeyboardMarkup(resize_keyboard=True)
    key.add(Buttons.back)
    if message.text == Buttons.timed[0]:
        await NewOrder.duration.set()
        await message.answer("How long will you have to complete it?", reply_markup=key)
    else:
        await NewOrder.timezone_city.set()
        await message.answer("When is your assignment due?\nPlease indicate your time zone OR "
                             "city in your answer. For example NYC DD/MM/YY and 8:30 PM", reply_markup=key)

@dp.message_handler(content_types=['text'], state=NewOrder.duration)
async def message_handler(message: types.Message, state: FSMContext):
    if await _back_client(message, state):
        return
    if len(message.text) > 50:
        await message.answer("Too long message, please try again")
        return
    key = types.ReplyKeyboardMarkup(resize_keyboard=True)
    key.add(Buttons.back)
    await state.update_data({'duration': message.text})
    await NewOrder.timezone_city.set()
    await message.answer("When is your assignment due?\nPlease indicate your time zone OR "
                         "city in your answer. For example NYC DD/MM/YY and 8:30 PM", reply_markup=key)

@dp.message_handler(content_types=['text'], state=NewOrder.timezone_city)
async def message_handler(message: types.Message, state: FSMContext):
    if await _back_client(message, state):
        return
    if len(message.text) > 50:
        await message.answer("Too long message, please try again")
        return
    key = types.ReplyKeyboardMarkup(resize_keyboard=True)
    key.add(Buttons.done)
    key.add(Buttons.back)
    await state.update_data({'timezone_city': message.text})
    await NewOrder.additions.set()
    await message.answer("Finally, is there anything else our tutor should know about your assignment? "
                         "If you have past exams, course outline, sample questions upload them here. "
                         "The more you attach the easier it will be for us to find a tutor that can help you. "
                         "When you are done press DONE.", reply_markup=key)

@dp.message_handler(content_types=['text', 'photo', 'document'], state=NewOrder.additions)
async def message_handler(message: types.Message, state: FSMContext):
    if await _back_client(message, state):
        return
    data = await state.get_data()
    if message.text == Buttons.done:
        price = get_price(data)
        key = types.ReplyKeyboardMarkup(resize_keyboard=True)
        key.add(Buttons.accept)
        key.add(Buttons.support)
        key.add(Buttons.back)
        await state.update_data({'price': price})
        await NewOrder.accepting.set()
        await message.answer(f"Our price for your order is ${price}. If you like it just click accept! "
                             "You won't have to pay until one of our tutors accepts your order.", reply_markup=key)
        return
    elif message.text:
        if len(message.text) > 500:
            await message.answer("Too long message, please try again")
            return
        if len(str(data.get('add_text'))) + len(message.text) > 500:
            await message.answer("The text that has already been sent is too long")
            return
        await state.update_data({'add_text': message.text})
    elif message.photo:
        if message.caption:
            if len(str(data.get('add_text'))) + len(message.caption) > 500:
                await message.answer("The text that has already been sent is too long")
            else:
                await state.update_data({'add_text': message.caption})
        await state.update_data({'add_photo': message.photo[-1].file_id})
    elif message.document:
        await state.update_data({'add_document': message.document.file_id})

@dp.message_handler(content_types=['text'], state=NewOrder.accepting)
async def message_handler(message: types.Message, state: FSMContext):
    if await _back_client(message, state):
        return
    await message.answer("Thanks! We are now looking for a tutor that will match your needs. "
                         "This shouldn't take longer than an hour. While you wait why don't you watch this "
                         "[video](https://youtu.be/ZZb_ZtjVxUQ) of cute puppies🐕!",
                         parse_mode='Markdown', reply_markup=client_keyboard())
    data = await state.get_data()
    await state.finish()

    text = data.get('add_text')
    photo = data.get('add_photo')
    document = data.get('add_document')
    media_links = ''

    if text:
        text_m = await bot.send_message(c.media_chat, text)
        media_links += f'[text](https://t.me/{c.media_chat[1:]}/{text_m.message_id}) '
    if photo:
        photo_m = await bot.send_photo(c.media_chat, photo)
        media_links += f'[photo](https://t.me/{c.media_chat[1:]}/{photo_m.message_id}) '
    if document:
        document_m = await bot.send_document(c.media_chat, document)
        media_links += f'[document](https://t.me/{c.media_chat[1:]}/{document_m.message_id}) '

    with open('prices.json', 'r') as f:
        prices = json.load(f)

    price_tutor = (data['price'] * prices['tutor_share']).__round__(2)
    due_date = data['timezone_city'].replace('_', '\\_').replace('*', '\\*').replace('`', '\\`').replace('[', '\\[')
    timed = data.get('duration', 'NO').replace('_', '\\_').replace('*', '\\*').replace('`', '\\`').replace('[', '\\[')
    text_to_channel = f"*{data['subject']}*\n" \
                      f"*{data['level']}*\n" \
                      f"*DUE DATE:* {due_date}\n" \
                      f"*TIMED:* {timed}\n" \
                      f"*SHORT ANSWER:* {data.get('s_answers', '0')}\n" \
                      f"*MULTIPLE CHOICE:* {data.get('m_choice', '0')}\n" \
                      f"*ADDITIONAL INFO WITH FILES:* {media_links}\n" \
                      f"*PRICE:* {price_tutor}"

    key = types.InlineKeyboardMarkup()
    key.add(types.InlineKeyboardButton("Take", callback_data=f"new{message.chat.id}_{data['price']}"))

    await bot.send_message(c.tutors_chat, text_to_channel, parse_mode='Markdown', reply_markup=key, disable_web_page_preview=True)

@dp.callback_query_handler(lambda callback_query: True)
async def callback_inline(callback_query: types.CallbackQuery, state: FSMContext):
    if callback_query.data[:6] == 'answer':
        await chat_answer(callback_query, state)
    elif callback_query.data[:3] == 'new':
        await generate_new_chat(callback_query)

@dp.callback_query_handler(lambda callback_query: True, state=Chat)
async def callback_inline(callback_query: types.CallbackQuery, state: FSMContext):
    if callback_query.data[:3] == 'new':
        await state.finish()
        await generate_new_chat(callback_query)

async def on_startup(dpc):
    await bot.set_webhook(WEBHOOK_URL)

async def on_shutdown(dpc):
    pass

if __name__ == '__main__':
    dp.loop.create_task(expited_chats_checker.check())
    if WEBHOOK:
        executor.start_webhook(dispatcher=dp, webhook_path=WEBHOOK_PATH,
                               on_startup=on_startup, on_shutdown=on_shutdown, host=WEBAPP_HOST, port=WEBAPP_PORT)
    else:
        executor.start_polling(dp, skip_updates=True)


import os, warnings
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'  # or any {'0', '1', '2'}
warnings.filterwarnings('ignore')

from trainer_VAE import VAETrainer
import matplotlib.pyplot as plt
import pandas as pd, numpy as np
import sys
import tensorflow as tf
from sklearn.preprocessing import MinMaxScaler, StandardScaler

def encode_hourly_data(latent_dim=64, num_hidden_layers=2):
    model_dir = f'./vae_model/latent_dim_{latent_dim}_layers_{num_hidden_layers}/'
    model_file_pref = f'model_'
    vae = VAETrainer.load_model(model_dir, model_file_pref)
    vae.summary()

    scaler = StandardScaler()
    train_data = np.load('./datasets/hourly_train.npy')
    scaler.fit(train_data)

    data = scaler.transform(train_data)
    n_data = len(data) // 24 - 1
    encode = np.zeros((24, n_data, vae.latent_dim))
    for i in range(0, len(data) - 24 - 1):
        day_data = data[i:i+24]
        encode[i % 24][i // 24] = vae.encode(day_data)
    print('data shape : ', data.shape)
    print('encoded shape : ', encode.shape)
    np.save('./datasets/encoded_train.npy', encode)

    test_data = np.load('./datasets/hourly_test.npy')
    data = scaler.transform(test_data)
    n_data = len(data) // 24
    encode = np.zeros((n_data, vae.latent_dim))
    for i in range(0, len(data), 24):
        day_data = data[i:i+24]
        encode[i//24] = vae.encode(day_data)
    print('data shape : ', data.shape)
    print('encoded shape : ', encode.shape)
    np.save('./datasets/encoded_test.npy', encode)

def encode_file_nonoverlap(vae, in_file):
    data = np.load(in_file)
    n_data = len(data) // 24
    encode = np.zeros((n_data, vae.latent_dim))
    for i in range(0, len(data), 24):
        day_data = data[i:i+24]
        encode[i//24] = vae.encode(day_data)
    print('data shape : ', data.shape)
    print('encoded shape : ', encode.shape)
    return encode

def encode_file_overlap(vae, in_file):
    data = np.load(in_file)
    n_data = len(data) // 24 - 1
    encode = np.zeros((24, n_data, vae.latent_dim))
    for i in range(0, len(data) - 24 - 1):
        day_data = data[i:i+24]
        encode[i % 24][i // 24] = vae.encode(day_data)
    print('data shape : ', data.shape)
    print('encoded shape : ', encode.shape)
    return encode