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 ...@@ -3,10 +3,12 @@ import discord
from discord.ext import commands from discord.ext import commands
from unit.manager import UnitManager from unit.manager import UnitManager
from util.command_error_handler import CommandErrorHandler
from settings import DISCORD_TOKEN from settings import DISCORD_TOKEN
loop = asyncio.get_event_loop() loop = asyncio.get_event_loop()
bot = commands.Bot(command_prefix='>') bot = commands.Bot(command_prefix='>')
bot.add_cog(CommandErrorHandler(bot))
bot.add_cog(UnitManager(bot)) bot.add_cog(UnitManager(bot))
@bot.event @bot.event
......
discord.py discord.py
python-dotenv python-dotenv
requests requests
pycryptodome pycryptodome
\ No newline at end of file discord-argparse==1.0.0
\ No newline at end of file
from util.api_requests import Api
import asyncio import asyncio
import discord import discord
import json import json
from discord.ext import commands from discord.ext import commands
from discord.user import User from discord.user import User
from unit.model import Unit 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): class UnitManager(commands.Cog):
...@@ -12,6 +14,92 @@ class UnitManager(commands.Cog): ...@@ -12,6 +14,92 @@ class UnitManager(commands.Cog):
self.bot = bot self.bot = bot
self.loop = loop or asyncio.get_event_loop() 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() @commands.command()
async def unitinfo(self, ctx, *, unit:str): async def unitinfo(self, ctx, *, unit:str):
...@@ -19,14 +107,12 @@ class UnitManager(commands.Cog): ...@@ -19,14 +107,12 @@ class UnitManager(commands.Cog):
data = None data = None
try: try:
unit_id = int(unit) data = await self._getUnit(unit)
data = await Api.get('/unit/id/{0}'.format(unit_id)) except ApiError as error:
except ValueError: return await self.handleApiError(ctx, error)
data = await Api.get('/unit/name/{0}'.format(unit))
if data==None: if data==None:
return await ctx.send('No unit found for: ' + unit) return await ctx.send('No unit found for: ' + unit)
unit_data = Unit(**data) unit_data = Unit(**data)
embed = discord.Embed(); embed = discord.Embed();
...@@ -131,7 +217,6 @@ class UnitManager(commands.Cog): ...@@ -131,7 +217,6 @@ class UnitManager(commands.Cog):
await msg.clear_reactions() await msg.clear_reactions()
await msg.edit(embed=embed) await msg.edit(embed=embed)
def createUnitPage(self, data, minI, maxI): def createUnitPage(self, data, minI, maxI):
embed = discord.Embed(color=0x19212d) embed = discord.Embed(color=0x19212d)
embed.set_author(name='TestBot') embed.set_author(name='TestBot')
...@@ -144,3 +229,7 @@ class UnitManager(commands.Cog): ...@@ -144,3 +229,7 @@ class UnitManager(commands.Cog):
embed.add_field(name='id\t| name\t| type\t| stars', value=units_str) embed.add_field(name='id\t| name\t| type\t| stars', value=units_str)
return embed 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 ...@@ -4,6 +4,17 @@ import requests
from util.crypto import Crypto from util.crypto import Crypto
from settings import API_URL_BASE 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: class Api:
@staticmethod @staticmethod
...@@ -16,10 +27,18 @@ class Api: ...@@ -16,10 +27,18 @@ class Api:
@staticmethod @staticmethod
async def post(url, data, cookies=None): async def post(url, data, cookies=None):
req_url = API_URL_BASE + url 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) return Api.status_code_handling(response)
@staticmethod @staticmethod
async def getWithUser(url, uid): async def getWithUser(url, uid):
enc_id = Crypto.encrypt(str(uid)) enc_id = Crypto.encrypt(str(uid))
...@@ -32,29 +51,45 @@ class Api: ...@@ -32,29 +51,45 @@ class Api:
enc_id = Crypto.encrypt(str(uid)) enc_id = Crypto.encrypt(str(uid))
cookies = {'discord_id': enc_id} cookies = {'discord_id': enc_id}
print('ENC: ' + 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 @staticmethod
def status_code_handling(response): def status_code_handling(response):
content = response.content.decode('utf-8');
if response.status_code >= 500: if response.status_code >= 500:
print('[!] [{0}] Server Error'.format(response.status_code)) error_str = '[!] [{0}] Server Error'.format(response.status_code)
return None print(error_str)
raise ApiError(response.status_code, content)
elif response.status_code == 404: elif response.status_code == 404:
print('[!] [{0}] URL not found'.format(response.status_code)) error_str = '[!] [{0}] URL not found'.format(response.status_code)
return None print(error_str)
raise ApiError(response.status_code, content)
elif response.status_code == 401: elif response.status_code == 401:
print('[!] [{0}] Authentication Failed'.format(response.status_code)) error_str = '[!] [{0}] Authentication Failed'.format(response.status_code);
return None print(error_str)
raise ApiError(response.status_code, content)
elif response.status_code >= 400: elif response.status_code >= 400:
print('[!] [{0}] Bad Request'.format(response.status_code)) error_str = '[!] [{0}] Bad Request'.format(response.status_code);
print(response.content ) print(error_str)
return None print(content)
raise ApiError(response.status_code, content)
elif response.status_code >= 300: elif response.status_code >= 300:
print('[!] [{0}] Unexpected redirect.'.format(response.status_code)) error_str = '[!] [{0}] Unexpected redirect.'.format(response.status_code)
return None print(error_str)
raise ApiError(response.status_code, content)
elif response.status_code == 204:
return True
elif response.status_code == 200: elif response.status_code == 200:
return json.loads(response.content.decode('utf-8')) return json.loads(response.content.decode('utf-8'))
else: else:
print('[?] Unexpected Error: [HTTP {0}]: Content: {1}'.format(response.status_code, response.content)) error_str = '[?] Unexpected Error: [HTTP {0}]: Content: {1}'.format(response.status_code, response.content)
return None print(error_str)
\ No newline at end of file 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'; 'use strict';
const Koa = require('koa'); const Koa = require('koa');
const Router = require('@koa/router');
const bodyParser = require('koa-body'); const bodyParser = require('koa-body');
const auth = require('./util/auth'); const auth = require('./util/auth');
const unitsRouter = require('./routes/units'); const unitsRouter = require('./unit/route');
const app = new Koa(); const app = new Koa();
const router = new Router();
router.use('/unit', unitsRouter.routes(), unitsRouter.allowedMethods());
app.use(bodyParser()); app.use(bodyParser());
//app.use(auth()); //app.use(auth());
app.use(unitsRouter.routes()).use(unitsRouter.allowedMethods()); app.use(router.routes()).use(router.allowedMethods());
app.listen(3000, () => console.log('Server Started')); 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', ' ...@@ -8,53 +8,65 @@ const unit_columns = ['name', 'type', 'stars', 'hp', 'pap', 'pd', 'sap', 'sd', '
units.getAll = async (context, next) =>{ 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{ try{
let data = await db.con.query(sql_text); const data = await db.con.query(sql_text);
context.response.body = {units: data}; context.response.body = {units: data};
}catch(error){ }catch(error){
console.log(error); console.log(error);
context.throw(400, 'INVALID_DATA'); context.throw(400, 'Invalid Data');
} }
} }
units.getUnitById = async (context, next) =>{ units.getUnit = async (context, next) =>{
let sql_text = `SELECT * FROM units WHERE id=${context.params.id}`;
try{ try{
let data = await db.con.query(sql_text); const units_data = await getUnit(context.params.term);
context.response.body = data[0]; if(units_data.length===0){
}catch(error){ context.throw(400, 'No Unit Found')
console.log(error); }
context.throw(400, 'INVALID_DATA'); context.response.body = units_data[0];
context.status = 200;
}
catch(error){
console.log(error)
context.throw(400, 'No Unit Found')
} }
} }
async function getUnit(term) {
units.getUnitByName = async (context, next) =>{ const unit_id = parseInt(term, 10);
let sql_text = `SELECT * FROM units WHERE name LIKE '%${context.params.name}%'`; if(isNaN(unit_id)){
try{ return await getUnitByName(term);
let data = await db.con.query(sql_text); }else{
context.response.body = data[0]; return await getUnitById(unit_id);
}catch(error){
console.log(error);
context.throw(400, 'INVALID_DATA');
} }
} }
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) =>{ units.insertUnit = async (context, next) =>{
let body = context.request.body; const body = context.request.body;
let column_text = 'name, type'; let column_text = 'name';
let value_text = `'${body.name}', '${body.type}'`; let value_text = `${db.con.escape(body.name)}`;
for (let i = 2; i < unit_columns.length; i++) { for (let i = 1; i < unit_columns.length; i++) {
const element = unit_columns[i]; const element = unit_columns[i];
if(body[element]!==undefined){ if(body[element]!==undefined){
column_text += ', ' + element; 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{ try{
let data = await db.con.query(sql_query); const data = await db.con.query(sql_query);
context.response.body = {status: 'success'}; context.response.status = 204
}catch(error){ }catch(error){
console.log(error); console.log(error);
context.throw(400, 'INVALID_DATA'); context.throw(400, 'INVALID_DATA');
...@@ -63,33 +75,39 @@ units.insertUnit = async (context, next) =>{ ...@@ -63,33 +75,39 @@ units.insertUnit = async (context, next) =>{
} }
units.modifyUnit = async (context, next) => { units.modifyUnit = async (context, next) => {
let body = context.request.body; const body = context.request.body;
let set_text = ''; if(!body){
if(body.name && body.type){ context.throw(400, 'No parameters')
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}'`;
} }
for (let i = 2; i < unit_columns.length; i++) { try{
const data = getUnitById(context.params.id)
if(data.length===0){
context.throw(400, 'No Unit exists')
}
}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]; const element = unit_columns[i];
if(body[element]!==undefined){ if(body[element]!==undefined){
if(set_text===''){ if(set_text===''){
set_text += `${element} = ${body[element]}`; set_text += `${element} = ${db.con.escape(body[element])}`;
}else{ }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{ try{
let data = await db.con.query(sql_query); const data = await db.con.query(sql_query, [context.params.id]);
context.response.body = {status: 'success'}; context.response.status = 204
}catch(error){ }catch(error){
console.log(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'); ...@@ -4,7 +4,7 @@ const Koa = require('koa');
const Router = require('@koa/router'); const Router = require('@koa/router');
const router = new Router(); const router = new Router();
const usersModel = require('../models/users'); const usersModel = require('./model');
router.get('/user/:id', usersModel.getUser); 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