Skip to content

Commit

Permalink
Merge pull request #28 from SiebeBaree/development
Browse files Browse the repository at this point in the history
v4.1
  • Loading branch information
SiebeBaree authored Jun 23, 2024
2 parents 5913829 + 77de54d commit 21a1e44
Show file tree
Hide file tree
Showing 59 changed files with 2,909 additions and 479 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ jobs:
WEBHOOK_URL: ${{ secrets.WEBHOOK_URL }}
NODE_ENV: ${{ secrets.NODE_ENV }}
NEXT_PUBLIC_BASE_URL: ${{ secrets.NEXT_PUBLIC_BASE_URL }}
API_TOKEN: ${{ secrets.API_TOKEN }}
API_URL: ${{ secrets.API_URL }}
steps:
- uses: actions/checkout@v4

Expand Down
2 changes: 2 additions & 0 deletions apps/api/src/router/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { Hono } from 'hono';
import vote from './vote';
import member from './member';

const app = new Hono();

app.route('/vote', vote);
app.route('/member', member);

export default app;
100 changes: 100 additions & 0 deletions apps/api/src/router/member.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { Hono } from 'hono';
import Member from '../schemas/member';

const app = new Hono();

type InvestmentBody = {
type: 'BUY' | 'SELL';
userId: string;
ticker: string;
amount: number;
price: number;
};

app.put('/investment', async (c) => {
const authKey = c.req.header('Authorization');
if (authKey !== process.env.TOKEN!) {
return c.json({ error: 'Invalid auth key' }, 401);
}

const body: InvestmentBody = await c.req.json();
if (!['BUY', 'SELL'].includes(body.type)) {
return c.json({ error: 'Invalid type' }, 400);
}

const member = await Member.findOne({ id: body.userId });
if (!member) {
return c.json({ error: 'Member not found' }, 404);
}

if (member.premium < 2) {
return c.json(
{
error: 'You need Coinz Pro to use the web interface. Please upgrade your account.',
},
403,
);
}

const ownedInvestment = member.investments.find((i) => i.ticker === body.ticker);
if (body.type === 'BUY') {
console.log(`${body.userId} bought ${body.amount} of ${body.ticker} for ${body.price}`);
if (ownedInvestment) {
await Member.updateOne(
{ id: member.id, 'investments.ticker': body.ticker },
{
$set: {
'investments.$.amount': `${Number.parseFloat(ownedInvestment.amount) + body.amount}`,
},
$inc: {
'investments.$.buyPrice': Math.round(body.price),
},
},
);
} else {
await Member.updateOne(
{ id: member.id },
{
$push: {
investments: {
ticker: body.ticker,
amount: body.amount,
buyPrice: body.price,
},
},
},
);
}
} else {
if (!ownedInvestment) {
return c.json({ error: 'Investment not found' }, 404);
}

console.log(`${body.userId} sold ${body.amount} of ${body.ticker} for ${body.price}`);
const ownedAmount = Number.parseFloat(ownedInvestment.amount);
if (Number.parseFloat(ownedInvestment.amount) <= body.amount || ownedAmount - body.amount < 0.00001) {
await Member.updateOne(
{ id: member.id },
{
$pull: { investments: { ticker: ownedInvestment.ticker } },
},
);
} else {
await Member.updateOne(
{ id: member.id, 'investments.ticker': body.ticker },
{
$set: {
'investments.$.amount': `${ownedAmount - body.amount}`,
},
$inc: {
'investments.$.buyPrice': -Math.floor((body.amount / ownedAmount) * ownedInvestment.buyPrice),
},
},
);
}
}

return c.json({ success: true }, 200);
});

export default app;
2 changes: 1 addition & 1 deletion apps/bot/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,11 @@
],
"prettier": "@repo/config/prettier/base.js",
"dependencies": {
"@upstash/redis": "^1.30.0",
"amqplib": "^0.10.4",
"bufferutil": "^4.0.8",
"discord-hybrid-sharding": "^2.1.9",
"discord.js": "^14.14.1",
"ioredis": "^5.3.2",
"moment": "^2.30.1",
"mongoose": "^8.3.1",
"obscenity": "^0.2.1",
Expand Down
68 changes: 45 additions & 23 deletions apps/bot/src/bot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { ActivityType, GatewayIntentBits, Partials } from 'discord.js';
import { connect } from 'mongoose';
import Bot from './domain/Bot';
import BotStats from './models/botStats';
import Investment from './models/investment';
import logger from './utils/logger';

(async () => {
Expand Down Expand Up @@ -34,32 +33,55 @@ import logger from './utils/logger';
}

bot.once('ready', async () => {
if (bot.cluster.id === bot.cluster.info.CLUSTER_COUNT - 1) {
const updateStatsInDb = async () => {
const guilds = [...bot.guilds.cache.values()];
let users = 0;
for (const guild of guilds) {
users += guild.memberCount;
}
const updateStatsInDb = async () => {
const guilds = [...bot.guilds.cache.values()];
let users = 0;
for (const guild of guilds) {
users += guild.memberCount;
}

const shards = bot.cluster.info.TOTAL_SHARDS;
const investmentsCount = await Investment.countDocuments();
const botStat = new BotStats({
guilds: guilds.length,
users: users,
shards: shards,
commands: bot.commands.size,
investments: investmentsCount,
updatedAt: new Date(),
const botStat = await BotStats.findOne({ updatedAt: { $gte: new Date().setHours(0, 0, 0, 0) } });
if (botStat) {
const clusterIndex = botStat.clusters.findIndex((cluster) => cluster.id === bot.cluster.id);
if (clusterIndex === -1) {
botStat.clusters.push({
id: bot.cluster.id,
guilds: guilds.length,
users: users,
totalShards: Number.parseInt(process.env.SHARDS_PER_CLUSTER!, 10),
});
await botStat.save();
} else {
await BotStats.updateOne(
{ _id: botStat._id, 'clusters.id': bot.cluster.id },
{
$set: {
'clusters.$.guilds': guilds.length,
'clusters.$.users': users,
'clusters.$.totalShards': Number.parseInt(process.env.SHARDS_PER_CLUSTER!, 10),
},
},
);
}
} else {
const newBotStat = new BotStats({
clusters: [
{
id: bot.cluster.id,
guilds: guilds.length,
users: users,
totalShards: Number.parseInt(process.env.SHARDS_PER_CLUSTER!, 10),
},
],
});
await botStat.save();
};
await newBotStat.save();
}
};

// Update stats in the database every 2 hours
setInterval(updateStatsInDb, 1000 * 60 * 120);
// Update stats in the database every day
setInterval(updateStatsInDb, 1000 * 60 * 60 * 24);

await updateStatsInDb();
}
await updateStatsInDb();
});

await bot.login(process.env.DISCORD_TOKEN!);
Expand Down
2 changes: 1 addition & 1 deletion apps/bot/src/commands/business/business/employee.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export default async function employee(
}

async function hire(client: Bot, interaction: ChatInputCommandInteraction, member: IMember, data: BusinessData) {
const MAX_EMPLOYEES = member.premium >= 2 ? 6 : 5;
const MAX_EMPLOYEES = member.premium >= 2 ? 6 : 3;
if (data.employee.position < Positions.Manager) {
await interaction.reply({
content: 'You need to be a manager or higher to hire employees.',
Expand Down
2 changes: 1 addition & 1 deletion apps/bot/src/commands/business/business/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ export default {
case 'supply':
await supply(client, interaction, data);
break;
case 'pay':
case 'payout':
await pay(client, interaction, data);
break;
case 'invest':
Expand Down
11 changes: 10 additions & 1 deletion apps/bot/src/commands/business/business/info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { Positions, type BusinessData } from '../../../lib/types';
import type { IBusiness } from '../../../models/business';
import Business from '../../../models/business';
import type { IMember } from '../../../models/member';
import { generateRandomString } from '../../../utils';
import { generateRandomString, getLevel } from '../../../utils';
import { removeMoney } from '../../../utils/money';

enum Category {
Expand Down Expand Up @@ -322,6 +322,15 @@ export default async function info(

business = null;
} else if (i.customId === 'business_create' && business === null) {
if (getLevel(member.experience) < 15) {
await i.deferUpdate();
await interaction.reply({
content: 'You need to be level 15 to create a business.',
ephemeral: true,
});
return;
}

const createNameInput = new TextInputBuilder()
.setCustomId('business-create-name')
.setLabel('Business Name')
Expand Down
12 changes: 10 additions & 2 deletions apps/bot/src/commands/business/business/invest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,15 @@ export default async function invest(

const amountStr = interaction.options.getString('amount', true);
const amount = parseStrToNum(amountStr);
const maxAmount = member.premium === 2 ? 10000 : 2000;
const maxAmount = member.premium === 2 ? 15000 : 1500;

if (Number.isNaN(amount)) {
await interaction.reply({
content: 'Please provide a valid number.',
ephemeral: true,
});
return;
}

if (amount < 100) {
await interaction.reply({
Expand All @@ -45,7 +53,7 @@ export default async function invest(
return;
} else if (amount > maxAmount) {
await interaction.reply({
content: `You cannot invest more than :coin: ${maxAmount}.${member.premium < 2 ? `(Upgrade to [premium](<${client.config.website}/premium>) to increase the limit to :coin: 10,000)` : ''}`,
content: `You cannot invest more than :coin: ${maxAmount}.${member.premium < 2 ? ` Upgrade to [premium](<${client.config.website}/premium>) to increase the limit to :coin: 15,000.` : ''}`,
ephemeral: true,
});
return;
Expand Down
5 changes: 1 addition & 4 deletions apps/bot/src/commands/business/business/pay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,7 @@ export default async function pay(client: Bot, interaction: ChatInputCommandInte
]);

await client.cooldown.setCooldown(interaction.user.id, COMMAND_NAME, COOLDOWN_TIME);
await interaction.reply({
embeds: [embed],
ephemeral: true,
});
await interaction.reply({ embeds: [embed] });

await Business.updateOne({ name: data.business.name }, { $inc: { balance: -totalAmount } });
for (const employee of data.business.employees) {
Expand Down
4 changes: 2 additions & 2 deletions apps/bot/src/commands/business/factory/_listItems.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ function createEmbed(client: Bot, items: FactoryItem[], data: BusinessData, page
}

description.push(
`${client.business.getItemString(item)}${stock ? `(Stock: ${stock.amount})` : ''}\n` +
`${client.business.getItemString(item)}${stock ? ` (Stock: ${stock.amount})` : ''}\n` +
(item.producable
? `> Produces ${item.amount} <:${item.itemId}:${item.emoteId}> every ${msToTime(item.duration * 1000)}\n> __Requirements:__ ${requirementsList.length > 0 ? requirementsList.join(', ') : 'No Requirements'}`
? `> Normal Sell Price: :coin: ${item.price}\n> Produces ${item.amount} <:${item.itemId}:${item.emoteId}> every ${msToTime(item.duration * 1000)}\n> Requirements: ${requirementsList.length > 0 ? requirementsList.join(', ') : 'No Requirements'}`
: `> Buy Price: :coin: ${item.price}`),
);
}
Expand Down
65 changes: 32 additions & 33 deletions apps/bot/src/commands/business/factory/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ import type { ColorResolvable, ChatInputCommandInteraction } from 'discord.js';
import type Bot from '../../../domain/Bot';
import type { Command } from '../../../domain/Command';
import { Positions, type BusinessData, type InventoryItem } from '../../../lib/types';
// import Business, { type IFactory } from '../../../models/business';
import Business from '../../../models/business';
import Business, { type IFactory } from '../../../models/business';
import { filter, getBusiness } from '../../../utils';
// import levelUp from './_levelUp';
import levelUp from './_levelUp';
import listItems from './_listItems';
import startProduction from './_startProduction';

Expand Down Expand Up @@ -205,22 +204,22 @@ async function pressButton_CollectProducts(client: Bot, data: BusinessData): Pro
}
}

// async function pressButton_BuyFactory(data: BusinessData): Promise<boolean> {
// const factoryId = data.business.factories.length;
// const cost = getFactoryCost(factoryId + 1);
// if (data.business.balance < cost) return false;

// const factory: IFactory = {
// factoryId,
// level: 0,
// production: '',
// status: 'standby',
// produceOn: 0,
// };

// await Business.updateOne({ name: data.business.name }, { $push: { factories: factory }, $inc: { balance: -cost } });
// return true;
// }
async function pressButton_BuyFactory(data: BusinessData): Promise<boolean> {
const factoryId = data.business.factories.length;
const cost = getFactoryCost(factoryId + 1);
if (data.business.balance < cost) return false;

const factory: IFactory = {
factoryId,
level: 0,
production: '',
status: 'standby',
produceOn: 0,
};

await Business.updateOne({ name: data.business.name }, { $push: { factories: factory }, $inc: { balance: -cost } });
return true;
}

export default {
data: {
Expand Down Expand Up @@ -271,20 +270,20 @@ export default {
await pressButton_CollectProducts(client, data);
await interaction.followUp({ content: 'You have collected all products.', ephemeral: true });
break;
// case 'factory_buy_factory':
// if (await pressButton_BuyFactory(data)) {
// await interaction.followUp({ content: 'You have bought a new factory.', ephemeral: true });
// } else {
// await interaction.followUp({
// content: 'You do not have enough balance to buy a new factory.',
// ephemeral: true,
// });
// }

// break;
// case 'factory_level_up':
// hasReplied = await levelUp(interaction, i, data);
// break;
case 'factory_buy_factory':
if (await pressButton_BuyFactory(data)) {
await interaction.followUp({ content: 'You have bought a new factory.', ephemeral: true });
} else {
await interaction.followUp({
content: 'You do not have enough balance to buy a new factory.',
ephemeral: true,
});
}

break;
case 'factory_level_up':
hasReplied = await levelUp(interaction, i, data);
break;
case 'factory_start_production':
hasReplied = await startProduction(client, interaction, i, data);
break;
Expand Down
Loading

0 comments on commit 21a1e44

Please sign in to comment.