Skip to content

Commit

Permalink
Merge pull request #11 from x-team/main
Browse files Browse the repository at this point in the history
Merging main
  • Loading branch information
ccmoralesj authored Feb 1, 2022
2 parents e5eac30 + 3b122cf commit df6d16b
Show file tree
Hide file tree
Showing 39 changed files with 1,268 additions and 19 deletions.
33 changes: 33 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# API
HOST=0.0.0.0
PORT=3010

# DATABASE
DB_USERNAME=postgres
DB_PASSWORD=*games-2021
DB_NAME=gameshq_api
DB_HOSTNAME=127.0.0.1
DB_PORT=5434

# THE ARENA APP
SLACK_ARENA_TOKEN=
SLACK_ARENA_SIGNING_SECRET=
# Private Channel
SLACK_ARENA_XHQ_CHANNEL=

# CAMPAIGN APP
SLACK_CAMPAIGN_SIGNING_SECRET=
SLACK_CAMPAIGN_TOKEN=
SLACK_NEUTRAL_ZONE_CHANNEL=

# THE TOWER APP
SLACK_TOWER_SIGNING_SECRET=
SLACK_TOWER_TOKEN=
SLACK_THE_TOWER_CHANNEL=

# FRONT-END-APP
FRONT_END_SIGNING_SECRET=
FRONT_END_APP_BOT_TOKEN=

# FIREBASE
GOOGLE_APPLICATION_CREDENTIALS="/path/to/yours/service/account/file/service-account-file.json"
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v16.10.0
6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,17 @@
"license": "ISC",
"dependencies": {
"@hapi/boom": "9.1.2",
"@hapi/catbox": "11.1.1",
"@hapi/catbox-memory": "5.0.1",
"@hapi/hapi": "20.1.5",
"@hapi/inert": "6.0.3",
"@hapi/vision": "6.1.0",
"@slack/client": "5.0.2",
"@types/catbox": "10.0.7",
"@types/catbox-memory": "4.0.0",
"dotenv": "10.0.0",
"fast-safe-stringify": "2.0.7",
"firebase-admin": "10.0.0",
"hapi-swagger": "14.2.1",
"indefinite": "2.4.1",
"joi": "17.4.0",
Expand All @@ -59,6 +64,7 @@
"devDependencies": {
"@types/boom": "7.3.1",
"@types/chai": "4.2.19",
"@types/hapi__catbox-memory": "4.1.3",
"@types/hapi__hapi": "20.0.9",
"@types/hapi__inert": "5.2.3",
"@types/hapi__vision": "5.5.3",
Expand Down
3 changes: 3 additions & 0 deletions src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ interface NameToType {
SLACK_TOWER_SIGNING_SECRET: string;
SLACK_CAMPAIGN_SIGNING_SECRET: string;
SLACK_THE_TOWER_CHANNEL: string;
FRONT_END_APP_BOT_TOKEN: string;
FRONT_END_SIGNING_SECRET: string;
GOOGLE_APPLICATION_CREDENTIALS: string;
}

export function getConfig<T extends keyof NameToType>(name: T): NameToType[T];
Expand Down
2 changes: 2 additions & 0 deletions src/games/arena/consts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ export enum ARENA_HEALTHKITS {
// REPOSITORIEs
export const ARENA_REPOSITORY_NAME = 'arena-repository';
export const ZONE_REPOSITORY_NAME = 'zone-repository';
export const WEAPON_REPOSITORY_NAME = 'weapon-repository';
export const ENEMY_REPOSITORY_NAME = 'enemy-repository';

// GAME
export const SEARCH_WEAPONS_SUCCESS_RATE = 0.8;
Expand Down
24 changes: 24 additions & 0 deletions src/games/arena/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ import {
ARENA_PLAYER_PERFORMANCE,
ARENA_SECONDARY_ACTIONS,
ChangeLocationParams,
ENEMY_REPOSITORY_NAME,
WEAPON_REPOSITORY_NAME,
ZONE_REPOSITORY_NAME,
} from '../consts';
import {
Expand Down Expand Up @@ -243,6 +245,28 @@ export function withArenaTransaction<T>(fn: (transaction: Transaction) => Promis
});
}

export function withWeaponTransaction<T>(fn: (transaction: Transaction) => Promise<T>) {
return withTransaction((transaction) => {
return fn(transaction).catch(async (error) => {
if (error instanceof GameError) {
error.addRepository(WEAPON_REPOSITORY_NAME);
}
throw error;
});
});
}

export function withEnemyTransaction<T>(fn: (transaction: Transaction) => Promise<T>) {
return withTransaction((transaction) => {
return fn(transaction).catch(async (error) => {
if (error instanceof GameError) {
error.addRepository(ENEMY_REPOSITORY_NAME);
}
throw error;
});
});
}

export function withZoneTransaction<T>(fn: (transaction: Transaction) => Promise<T>) {
return withTransaction((transaction) => {
return fn(transaction).catch(async (error) => {
Expand Down
23 changes: 23 additions & 0 deletions src/games/general/commands/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { getGameResponse } from '../../utils';
import { GAMES_SLACK_COMMANDS } from '../consts';

import { register } from './register';

interface GamesHQSwitchCommandOptions {
command: string;
commandText: string;
slackId: string;
channelId: string;
triggerId: string;
}

export function gamesSwitchCommand({ command, slackId }: GamesHQSwitchCommandOptions) {
switch (command) {
// ADMIN
case GAMES_SLACK_COMMANDS.REGISTER:
return register(slackId);

default:
return getGameResponse('Invalid command.');
}
}
37 changes: 37 additions & 0 deletions src/games/general/commands/register.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import Boom from '@hapi/boom';

import { USER_ROLE_LEVEL } from '../../../consts/model';
import { findOrganizationByName } from '../../../models/Organization';
import { createUser, userExists } from '../../../models/User';
import { getGameResponse, getSlackUserInfo } from '../../utils';

export const register = async (slackUserId: string) => {
const exists = await userExists(slackUserId);
if (exists) {
return getGameResponse(`Your user is already registered.`);
}

const xteamOrganization = await findOrganizationByName('x-team');
const { user } = await getSlackUserInfo(slackUserId);

if (!user || !user.profile || !xteamOrganization) {
throw Boom.badRequest(`Failed to create new user on GamesHQ.`);
}
const { profile } = user;
const { email, image_512 } = profile;

await createUser({
email: email,
displayName: user.real_name,
firebaseUserUid: null,
profilePictureUrl: image_512,
slackId: user.id,
_teamId: null,
_roleId: USER_ROLE_LEVEL.USER,
_organizationId: xteamOrganization?.id,
});

return getGameResponse(
`Your e-mail _${email}_ is now registered to all our games :partyparrot: `
);
};
4 changes: 4 additions & 0 deletions src/games/general/consts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export enum GAMES_SLACK_COMMANDS {
// USER COMMANDS
REGISTER = '/games-register',
}
31 changes: 31 additions & 0 deletions src/games/tower/repositories/arena/zonesRepository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { createOrUpdateArenaZone, deleteZoneById } from '../../../../models/ArenaZone';
import { ARENA_ZONE_RING } from '../../../arena/consts';
import { withZoneTransaction } from '../../../arena/utils';

export interface IZoneEditorData {
id?: number;
name: string;
emoji: string;
ring: string;
isArchived: boolean;
}

export const upsertZone = async (data: IZoneEditorData) => {
return withZoneTransaction(async () => {
const values = {
...(data.id && { id: data.id }),
name: data.name,
emoji: data.emoji,
isArchived: data.isArchived,
ring: data.ring as ARENA_ZONE_RING,
isActive: data.isArchived,
};
return createOrUpdateArenaZone(values);
});
};

export const deleteZone = async (zoneId: number) => {
return withZoneTransaction(async (transaction) => {
return deleteZoneById(zoneId, transaction);
});
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
removeEnemyFromFloorBattlefields,
} from '../../../../../../models/TowerFloorBattlefieldEnemy';
import {
addTowerFloorEnemies,
addTowerFloorEnemy,
findTowerFloorEnemyById,
} from '../../../../../../models/TowerFloorEnemy';
Expand Down Expand Up @@ -135,6 +136,29 @@ export async function removeEnemyFromFloor(userRequesting: User, towerEnemyId: n
});
}

export async function addEnemiesToFloor(
userRequesting: User,
floorNumber: number,
enemyIds: number[]
) {
return withTowerTransaction(async (transaction) => {
const isAdmin = adminAction(userRequesting);
if (!isAdmin) {
return getGameError(towerCommandReply.adminsOnly());
}
const activeTower = await activeTowerHandler(transaction);
if (!(activeTower instanceof Game)) {
return activeTower as GameResponse;
}
const towerFloor = activeTower._tower?._floors?.find((floor) => floor.number === floorNumber);
if (!towerFloor) {
return getGameError(towerCommandReply.floorNumberNotValid());
}
await addTowerFloorEnemies(towerFloor.id, enemyIds, transaction);
return;
});
}

export async function addEnemyToFloor(userRequesting: User, floorNumber: number, enemyId: number) {
return withTowerTransaction(async (transaction) => {
const isAdmin = adminAction(userRequesting);
Expand Down
50 changes: 50 additions & 0 deletions src/games/tower/repositories/tower/enemyRepository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { createOrUpdateEnemy, deleteEnemyById } from '../../../../models/Enemy';
import { createEnemyPattern, existsEnemyPattern } from '../../../../models/EnemyPattern';
import { withEnemyTransaction } from '../../../arena/utils';
import { AbilityProperty } from '../../../classes/GameAbilities';

export interface IEnemyEditorData {
id?: number;
name: string;
gifUrl: string;
emoji: string;
abilitiesJSON: AbilityProperty;
health: number;
isBoss: boolean;
majorDamageRate: number;
minorDamageRate: number;
actionPattern: string;
}

export const upsertEnemy = async (data: IEnemyEditorData) => {
return withEnemyTransaction(async (transaction) => {
const enemyPatternExists = await existsEnemyPattern(data.actionPattern, transaction);
let enemyPatternId = data.actionPattern;
if (!enemyPatternExists) {
const newPattern = await createEnemyPattern(data.actionPattern);
enemyPatternId = newPattern.id;
}

return createOrUpdateEnemy(
{
...(data.id && { id: data.id }),
abilitiesJSON: data.abilitiesJSON,
emoji: data.emoji,
gifUrl: data.gifUrl,
name: data.name,
health: data.health,
isBoss: data.isBoss,
majorDamageRate: data.majorDamageRate,
minorDamageRate: data.minorDamageRate,
_enemyPatternId: enemyPatternId,
},
transaction
);
});
};

export const deleteEnemy = async (enemyId: number) => {
return withEnemyTransaction(async (transaction) => {
return deleteEnemyById(enemyId, transaction);
});
};
12 changes: 12 additions & 0 deletions src/games/tower/repositories/tower/floorRepository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { findAdmin } from '../../../../models/User';

import { addEnemiesToFloor } from './actions/admin/tower-floors-operations';

export const addEnemies = async (floorNumber: number, enemyIds: number[]) => {
const adminUser = await findAdmin();
if (!adminUser) {
return;
}

await addEnemiesToFloor(adminUser, floorNumber, enemyIds);
};
41 changes: 41 additions & 0 deletions src/games/tower/repositories/tower/towerRepository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { startTowerGame } from '../../../../models/TowerGame';
import { findAdmin } from '../../../../models/User';
import { withTowerTransaction } from '../../utils';
import { endGame, openOrCloseTowerGates } from './actions/admin/create-or-finish-game';

export interface ICreateTowerGameData {
name: string;
height: number;
}

export const createTowerGame = async (data: ICreateTowerGameData) => {
return withTowerTransaction(async (transaction) => {
await startTowerGame(
{
name: data.name,
height: data.height,
isOpen: false,
lunaPrize: 0,
coinPrize: 0,
_createdById: 1,
},
transaction
);
});
};

export const endCurrentTowerGame = async () => {
const adminUser = await findAdmin();
if (!adminUser) {
return;
}
return endGame(adminUser);
};

export const openOrCloseTower = async (open: boolean) => {
const adminUser = await findAdmin();
if (!adminUser) {
return;
}
return openOrCloseTowerGates(adminUser, open);
};
Loading

0 comments on commit df6d16b

Please sign in to comment.