Commit 2141fad0 authored by Geovanny's avatar Geovanny

Refactored server. Refactored API request in discord. Added insert and modify…

Refactored server. Refactored API request in discord. Added insert and modify unit on the discord bot
parent 8d680c37
......@@ -3,10 +3,12 @@ import discord
from discord.ext import commands
from unit.manager import UnitManager
from util.command_error_handler import CommandErrorHandler
from settings import DISCORD_TOKEN
loop = asyncio.get_event_loop()
bot = commands.Bot(command_prefix='>')
bot.add_cog(CommandErrorHandler(bot))
bot.add_cog(UnitManager(bot))
@bot.event
......
......@@ -2,3 +2,4 @@ discord.py
python-dotenv
requests
pycryptodome
discord-argparse==1.0.0
\ No newline at end of file
from util.api_requests import Api
import asyncio
import discord
import json
from discord.ext import commands
from discord.user import User
from unit.model import Unit
from util.api_requests import Api, ApiError
from .unit_parameters import *
from discord_argparse import ArgumentConverter
class UnitManager(commands.Cog):
......@@ -12,6 +14,92 @@ class UnitManager(commands.Cog):
self.bot = bot
self.loop = loop or asyncio.get_event_loop()
async def handleApiError(self, ctx, error):
if error.error_code == 400:
return await ctx.send(error.message)
else:
return await ctx.send('Unexpected error. Please try again')
#@commands.HelpCommand()
async def send_cog_help(self, cog):
print('Hello')
self.add_command_formatting(command)
for name, param in command.clean_params.items():
if isinstance(param.annotation, ArgumentConverter):
arguments = param.annotation.arguments
if not arguments:
continue
self.paginator.add_line("Arguments:")
max_size = max(len(name) for name in arguments)
for name, argument in arguments.items():
entry = "{0}{1:<{width}} {2}".format(self.indent * " ", name, argument.doc, width=max_size)
self.paginator.add_line(self.shorten_text(entry))
self.paginator.close_page()
await self.send_pages()
async def send_bot_help(self, command):
print('There')
self.add_command_formatting(command)
for name, param in command.clean_params.items():
if isinstance(param.annotation, ArgumentConverter):
arguments = param.annotation.arguments
if not arguments:
continue
self.paginator.add_line("Arguments:")
max_size = max(len(name) for name in arguments)
for name, argument in arguments.items():
entry = "{0}{1:<{width}} {2}".format(self.indent * " ", name, argument.doc, width=max_size)
self.paginator.add_line(self.shorten_text(entry))
self.paginator.close_page()
await self.send_pages()
@commands.command()
async def modifyUnit(self, ctx, unit:str, *, params:modify_param_converter=modify_param_converter.defaults()):
"""Modify units by name or id. Specify parameters to be modified"""
if not params:
return await ctx.send('Some parameters have to be provided')
data = None
try:
data = await self._getUnit(unit)
except ApiError as error:
return await self.handleApiError(ctx, error)
await ctx.send('Do you want to modify \'{0}\'? Yes to confirm'.format(data['name']))
def check(m):
return m.channel == ctx.channel and m.author == ctx.message.author
msg = ''
try:
msg = await self.bot.wait_for('message', timeout=30.0, check=check)
except asyncio.TimeoutError:
return await ctx.send('Operation aborted')
if msg.content.lower() != 'yes':
return await ctx.send('Operation aborted')
try:
await Api.put('/unit/{0}'.format(data['id']), params)
return await ctx.send('Succesfully modifed {0}'.format(data['name']))
except ApiError as error:
return await self.handleApiError(ctx, error)
@commands.command()
async def insertUnit(self, ctx, name:str, *, params:insert_param_converter=insert_param_converter.defaults()):
"""Insert unit by name. Additional parameters can be added"""
unit_data = params;
unit_data['name'] = name;
try:
await Api.post('/unit', unit_data)
return await ctx.send('Unit added successfully')
except ApiError as error:
return await self.handleApiError(ctx, error)
@commands.command()
async def unitinfo(self, ctx, *, unit:str):
......@@ -19,14 +107,12 @@ class UnitManager(commands.Cog):
data = None
try:
unit_id = int(unit)
data = await Api.get('/unit/id/{0}'.format(unit_id))
except ValueError:
data = await Api.get('/unit/name/{0}'.format(unit))
data = await self._getUnit(unit)
except ApiError as error:
return await self.handleApiError(ctx, error)
if data==None:
return await ctx.send('No unit found for: ' + unit)
unit_data = Unit(**data)
embed = discord.Embed();
......@@ -131,7 +217,6 @@ class UnitManager(commands.Cog):
await msg.clear_reactions()
await msg.edit(embed=embed)
def createUnitPage(self, data, minI, maxI):
embed = discord.Embed(color=0x19212d)
embed.set_author(name='TestBot')
......@@ -144,3 +229,7 @@ class UnitManager(commands.Cog):
embed.add_field(name='id\t| name\t| type\t| stars', value=units_str)
return embed
async def _getUnit(self, search_term):
return await Api.get('/unit/{0}'.format(search_term))
\ No newline at end of file
from discord_argparse import *
insert_param_converter = ArgumentConverter(
type = OptionalArgument(
str,
doc='Type of Unit',
),
stars = OptionalArgument(
int,
doc='Stars of unit',
),
hp = OptionalArgument(
int,
doc='Health points',
),
pap = OptionalArgument(
int,
doc='Piercing armor penetration',
),
pd = OptionalArgument(
int,
doc='Piercing damage',
),
pdf = OptionalArgument(
int,
doc='Piercing defense',
),
sap = OptionalArgument(
int,
doc='Slashing armor penetration',
),
sd = OptionalArgument(
int,
doc='Slasing damage',
),
sdf = OptionalArgument(
int,
doc='Slasing defense',
),
bap = OptionalArgument(
int,
doc='Blunt armor penetration',
),
bd = OptionalArgument(
int,
doc='Blunt damage',
),
bdf = OptionalArgument(
int,
doc='Blunt defense',
),
ld = OptionalArgument(
int,
doc='Required leadership',
),
tc = OptionalArgument(
int,
doc='Troop count of unit',
),
hl = OptionalArgument(
int,
doc='Hero level required',
),
speed = OptionalArgument(
int,
doc='Speed of unit',
),
range = OptionalArgument(
int,
doc='Range of unit',
),
ammo = OptionalArgument(
int,
doc='Ammo if unit',
),
labour = OptionalArgument(
int,
doc='Resourse collection labour',
),
img = OptionalArgument(
str,
doc='Url of unit image',
),
vet_img = OptionalArgument(
str,
doc='Url of veterany image',
)
)
modify_param_converter = ArgumentConverter(
name = OptionalArgument(
str,
doc='New name',
),
type = OptionalArgument(
str,
doc='Type of Unit',
),
stars = OptionalArgument(
int,
doc='Stars of unit',
),
hp = OptionalArgument(
int,
doc='Health points',
),
pap = OptionalArgument(
int,
doc='Piercing armor penetration',
),
pd = OptionalArgument(
int,
doc='Piercing damage',
),
pdf = OptionalArgument(
int,
doc='Piercing defense',
),
sap = OptionalArgument(
int,
doc='Slashing armor penetration',
),
sd = OptionalArgument(
int,
doc='Slasing damage',
),
sdf = OptionalArgument(
int,
doc='Slasing defense',
),
bap = OptionalArgument(
int,
doc='Blunt armor penetration',
),
bd = OptionalArgument(
int,
doc='Blunt damage',
),
bdf = OptionalArgument(
int,
doc='Blunt defense',
),
ld = OptionalArgument(
int,
doc='Required leadership',
),
tc = OptionalArgument(
int,
doc='Troop count of unit',
),
hl = OptionalArgument(
int,
doc='Hero level required',
),
speed = OptionalArgument(
int,
doc='Speed of unit',
),
range = OptionalArgument(
int,
doc='Range of unit',
),
ammo = OptionalArgument(
int,
doc='Ammo if unit',
),
labour = OptionalArgument(
int,
doc='Resourse collection labour',
),
img = OptionalArgument(
str,
doc='Url of unit image',
),
vet_img = OptionalArgument(
str,
doc='Url of veterany image',
)
)
\ No newline at end of file
......@@ -4,6 +4,17 @@ import requests
from util.crypto import Crypto
from settings import API_URL_BASE
class ApiError(Exception):
def __init__(self, error_code:int, message:str=None):
self.error_code = error_code
self.message = message
def __str__(self):
if self.message:
return 'ApiError, {0} '.format(self.message)
else:
return 'ApiError has been raised'
class Api:
@staticmethod
......@@ -16,10 +27,18 @@ class Api:
@staticmethod
async def post(url, data, cookies=None):
req_url = API_URL_BASE + url
response = requests.get(req_url, json=data, cookies=cookies)
response = requests.post(req_url, json=data, cookies=cookies)
return Api.status_code_handling(response)
@staticmethod
async def put(url, data, cookies=None):
req_url = API_URL_BASE + url
response = requests.put(req_url, json=data, cookies=cookies)
return Api.status_code_handling(response)
@staticmethod
async def getWithUser(url, uid):
enc_id = Crypto.encrypt(str(uid))
......@@ -32,29 +51,45 @@ class Api:
enc_id = Crypto.encrypt(str(uid))
cookies = {'discord_id': enc_id}
print('ENC: ' + enc_id)
return await Api.get(url, data, cookies)
return await Api.post(url, data, cookies)
@staticmethod
async def putWithUser(url, data, uid):
enc_id = Crypto.encrypt(str(uid))
cookies = {'discord_id': enc_id}
print('ENC: ' + enc_id)
return await Api.put(url, data, cookies)
@staticmethod
def status_code_handling(response):
content = response.content.decode('utf-8');
if response.status_code >= 500:
print('[!] [{0}] Server Error'.format(response.status_code))
return None
error_str = '[!] [{0}] Server Error'.format(response.status_code)
print(error_str)
raise ApiError(response.status_code, content)
elif response.status_code == 404:
print('[!] [{0}] URL not found'.format(response.status_code))
return None
error_str = '[!] [{0}] URL not found'.format(response.status_code)
print(error_str)
raise ApiError(response.status_code, content)
elif response.status_code == 401:
print('[!] [{0}] Authentication Failed'.format(response.status_code))
return None
error_str = '[!] [{0}] Authentication Failed'.format(response.status_code);
print(error_str)
raise ApiError(response.status_code, content)
elif response.status_code >= 400:
print('[!] [{0}] Bad Request'.format(response.status_code))
print(response.content )
return None
error_str = '[!] [{0}] Bad Request'.format(response.status_code);
print(error_str)
print(content)
raise ApiError(response.status_code, content)
elif response.status_code >= 300:
print('[!] [{0}] Unexpected redirect.'.format(response.status_code))
return None
error_str = '[!] [{0}] Unexpected redirect.'.format(response.status_code)
print(error_str)
raise ApiError(response.status_code, content)
elif response.status_code == 204:
return True
elif response.status_code == 200:
return json.loads(response.content.decode('utf-8'))
else:
print('[?] Unexpected Error: [HTTP {0}]: Content: {1}'.format(response.status_code, response.content))
return None
\ No newline at end of file
error_str = '[?] Unexpected Error: [HTTP {0}]: Content: {1}'.format(response.status_code, response.content)
print(error_str)
raise ApiError(response.status_code, content)
\ No newline at end of file
from discord.ext import commands
import discord_argparse.errors as da_errors
class CommandErrorHandler(commands.Cog):
def __init__(self, bot):
self.bot = bot
@commands.Cog.listener()
async def on_command_error(self, ctx, error):
"""The event triggered when an error is raised while invoking a command.
ctx : Context
error : Exception"""
# This prevents any commands with local handlers being handled here in on_command_error.
if hasattr(ctx.command, 'on_error'):
return
ignored = (commands.CommandNotFound, commands.UserInputError)
discord_argparse_errors = (da_errors.InvalidArgumentValueError, da_errors.UnknownArgumentError)
if isinstance(error, discord_argparse_errors):
return await ctx.send(error)
# Allows us to check for original exceptions raised and sent to CommandInvokeError.
# If nothing is found. We keep the exception passed to on_command_error.
error = getattr(error, 'original', error)
# Anything in ignored will return and prevent anything happening.
if isinstance(error, ignored):
return
elif isinstance(error, commands.DisabledCommand):
return await ctx.send(f'{ctx.command} has been disabled.')
elif isinstance(error, commands.NoPrivateMessage):
try:
return await ctx.author.send(f'{ctx.command} can not be used in Private Messages.')
except:
pass
# For this error example we check to see where it came from...
elif isinstance(error, commands.BadArgument):
if ctx.command.qualified_name == 'tag list': # Check if the command being invoked is 'tag list'
return await ctx.send('I could not find that member. Please try again.')
'use strict';
const Koa = require('koa');
const Router = require('@koa/router');
const bodyParser = require('koa-body');
const auth = require('./util/auth');
const unitsRouter = require('./routes/units');
const unitsRouter = require('./unit/route');
const app = new Koa();
const router = new Router();
router.use('/unit', unitsRouter.routes(), unitsRouter.allowedMethods());
app.use(bodyParser());
//app.use(auth());
app.use(unitsRouter.routes()).use(unitsRouter.allowedMethods());
app.use(router.routes()).use(router.allowedMethods());
app.listen(3000, () => console.log('Server Started'));
\ No newline at end of file
'use strict';
const Koa = require('koa');
const Router = require('@koa/router');
const router = new Router();
const unitsModel = require('../models/units');
router.get('/unit/all', unitsModel.getAll);
router.get('/unit/id/:id', unitsModel.getUnitById);
router.get('/unit/name/:name', unitsModel.getUnitByName);
router.post('/unit/insert', unitsModel.insertUnit);
router.post('/unit/modify', unitsModel.modifyUnit);
module.exports = router;
\ No newline at end of file
......@@ -8,53 +8,65 @@ const unit_columns = ['name', 'type', 'stars', 'hp', 'pap', 'pd', 'sap', 'sd', '
units.getAll = async (context, next) =>{
let sql_text = 'SELECT * FROM units ORDER BY name ASC';
const sql_text = 'SELECT * FROM units ORDER BY name ASC';
try{
let data = await db.con.query(sql_text);
const data = await db.con.query(sql_text);
context.response.body = {units: data};
}catch(error){
console.log(error);
context.throw(400, 'INVALID_DATA');
context.throw(400, 'Invalid Data');
}
}
units.getUnitById = async (context, next) =>{
let sql_text = `SELECT * FROM units WHERE id=${context.params.id}`;
units.getUnit = async (context, next) =>{
try{
let data = await db.con.query(sql_text);
context.response.body = data[0];
}catch(error){
console.log(error);
context.throw(400, 'INVALID_DATA');
const units_data = await getUnit(context.params.term);
if(units_data.length===0){
context.throw(400, 'No Unit Found')
}
context.response.body = units_data[0];
context.status = 200;
}
catch(error){
console.log(error)
context.throw(400, 'No Unit Found')
}
}
units.getUnitByName = async (context, next) =>{
let sql_text = `SELECT * FROM units WHERE name LIKE '%${context.params.name}%'`;
try{
let data = await db.con.query(sql_text);
context.response.body = data[0];
}catch(error){
console.log(error);
context.throw(400, 'INVALID_DATA');
async function getUnit(term) {
const unit_id = parseInt(term, 10);
if(isNaN(unit_id)){
return await getUnitByName(term);
}else{
return await getUnitById(unit_id);
}
}
async function getUnitById(id){
const sql_text = 'SELECT * FROM units WHERE id= ?';
const data = await db.con.query(sql_text, [id]);
return data;
}
async function getUnitByName(name){
const sql_text = 'SELECT * FROM units WHERE name LIKE ?';
const data = await db.con.query(sql_text, [`%${context.params.name}%`]);
return data;
}
units.insertUnit = async (context, next) =>{
let body = context.request.body;
let column_text = 'name, type';
let value_text = `'${body.name}', '${body.type}'`;
for (let i = 2; i < unit_columns.length; i++) {
const body = context.request.body;
let column_text = 'name';
let value_text = `${db.con.escape(body.name)}`;
for (let i = 1; i < unit_columns.length; i++) {
const element = unit_columns[i];
if(body[element]!==undefined){
column_text += ', ' + element;
value_text += ', ' +body[element];
value_text += ', ' + db.con.escape(body[element]);
}
}
let sql_query = 'INSERT INTO units (' + column_text + ') VALUES (' + value_text + ');';
const sql_query = 'INSERT INTO units (' + column_text + ') VALUES (' + value_text + ');';
try{
let data = await db.con.query(sql_query);
context.response.body = {status: 'success'};
const data = await db.con.query(sql_query);
context.response.status = 204
}catch(error){
console.log(error);
context.throw(400, 'INVALID_DATA');
......@@ -63,33 +75,39 @@ units.insertUnit = async (context, next) =>{
}
units.modifyUnit = async (context, next) => {
let body = context.request.body;
let set_text = '';
if(body.name && body.type){
set_text += `name = '${body.name}', type = '${body.type}'`;
}else if(body.name){
set_text += `name = '${body.name}'`;
}else if(body.type){
set_text += `type = '${body.type}'`;
const body = context.request.body;
if(!body){
context.throw(400, 'No parameters')
}
try{
const data = getUnitById(context.params.id)
if(data.length===0){
context.throw(400, 'No Unit exists')
}
for (let i = 2; i < unit_columns.length; i++) {
}catch(error){
console.log(error);
context.throw(400, 'Invalid unit id')
}
let set_text = '';
for (let i = 0; i < unit_columns.length; i++) {
const element = unit_columns[i];
if(body[element]!==undefined){
if(set_text===''){
set_text += `${element} = ${body[element]}`;
set_text += `${element} = ${db.con.escape(body[element])}`;
}else{
set_text += `, ${element} = ${body[element]}`;
set_text += `, ${element} = ${db.con.escape(body[element])}`;
}
}
}
let sql_query = `UPDATE units SET ${set_text} WHERE id = ${body.id}`;
const sql_query = `UPDATE units SET ${set_text} WHERE id = ?`;
try{
let data = await db.con.query(sql_query);
context.response.body = {status: 'success'};
const data = await db.con.query(sql_query, [context.params.id]);
context.response.status = 204
}catch(error){
console.log(error);
context.throw(400, 'INVALID_DATA');
context.throw(400, 'Invalid Data');
}
}
......
'use strict';
const Koa = require('koa');
const Router = require('@koa/router');
const router = new Router();
const unitsModel = require('./model');
router.get('/all', unitsModel.getAll);
router.get('/:term', unitsModel.getUnit);
router.post('/', unitsModel.insertUnit);
router.put('/:id', unitsModel.modifyUnit);
module.exports = router;
\ No newline at end of file
......@@ -4,7 +4,7 @@ const Koa = require('koa');
const Router = require('@koa/router');
const router = new Router();
const usersModel = require('../models/users');
const usersModel = require('./model');
router.get('/user/:id', usersModel.getUser);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment