Skip to content

Commit

Permalink
#skip-ci open source prep
Browse files Browse the repository at this point in the history
  • Loading branch information
jgaribsin committed Mar 21, 2024
1 parent e11fbb4 commit a41466b
Show file tree
Hide file tree
Showing 20 changed files with 98 additions and 125 deletions.
98 changes: 83 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,27 +1,95 @@
# Welcome!
# HellCom

o/
This is the repo for the Discord bot HellCom StratDef Network, a Helldivers 2 community bot. This project is meant to be a utility/helper bot for any Helldivers Discords!

HellCom StratDef Network is the all-in-one solution for your Helldivers 2 server, delivering in-game updates right to your Discord! HellCom features live updates about the in-game war, easy and convenient ways to check in with the game's progress, and allows you to get notifications for any in-game event, so you can stay up to date on your fellow Helldivers' progress while you're offline!
HellCom features live updates about the in-game war, easy and convenient ways to check in with the game's progress, and allows you to get notifications for any in-game event, so you can stay up to date on your fellow Helldivers' progress while you're offline!

HellCom will be continuously updated and improved. It is a community-driven project, so if there's a feature you'd like to see, feel free to suggest it in our Discord! Improvements and addition suggestions are always welcome =)

You can add it to your own server using **[this link (click)](https://discord.com/api/oauth2/authorize?client_id=1213944670288347176&permissions=274878221376&scope=applications.commands%20bot)**.
You can add it to your own server using **[this link (click)](https://discord.com/application-directory/1213944670288347176)**.

## Features
## Commands

-`/planet`: Check information for any in-game planet, with autocomplete!
-`/campaign`: Information about ongoing campaigns
-`/events`: Sends the latest in-game events (dispatch messages)
-`/subscribe`:
- `/subscribe updates`: Get announcements about in-game war progress automatically
- `/subscribe status`: Sends a message with quick summary of campaign progress and the current major order, automatically updated

> Thanks for reading, have an awesome day!
- `/campaign`: Campaign-related information (playable planets)
- `/campaign list`: Gives an overview of all currently active planets with some stats for each one
- `/campaign most`: Gives an overview of the campaign with the most active players
- `/campaign info <PLANET_NAME>`: Gives an overview of a campaign on a _specified_ planet. Has automcomplete for active campaigns
- `/community`: Highlights other community projects with a description and link(s)
- `/discord`: Information about HellCom's partnered/support server, as well as support links (eg. donations, voting)
- `/dispatches`: Shows a list of in-game dispatch messages with timestamps
- `/events`: In-game ""events"" -- kind of outdated, this was made before the NewsFeed API existed
- `/events all`: Shows all active events
- `/events latest`: Shows the most recent event
- `/history`: Shows historical data, currently via line graphs
- `/history players`: Generates a graph for historical player counts (per faction, and total)
- `/map`: Generates galactic maps dynamically!
- `/map galaxy`: Shows an image of the entire galaxy with planets mapped onto it -- names are shown for planets we don't control
- `/map planet <PLANET_NAME>`: Similar to above, but zooms in on a specified planet -- additionally shows campaign info (players, lib%)
- `/planet`
- `/planet list`: (Gotta move this) Shows a summary of the current in-game status such as campaigns (+progress) and major order (if there is one)
- `/planet info <PLANET_NAME>`: Shows some information about a specified planet -- not limited to active campaigns
- `/subscribe`
- `/subscribe event`: Allows users to "subscribe" a channel to event updated. The bot will then post updates about the war in that channel as it happens -- stuff like winning (or losing!) a campaign, getting access to a new planet, new dispatch messages, new major orders.
- `/subscribe status`: Sends a message in the channel the command was used, displays summary information (identical to `/planet list`) and will update after certain intervals automatically.

If you notice any issues, or have a suggestion, feel free to contact me via Discord @`theyodastream`.

## Terms of Service
## Development

Created using [TypeScript](https://www.typescriptlang.org/) and [discord.js](https://discord.js.org/#/), using [Supabase](https://supabase.com/) for a PG db/backend, and [New Relic](https://newrelic.com/platform) for logging/metrics. Using [gts (Google TypeScript Style)](https://github.com/google/gts) for styling/formatting.

Deployed using Docker (currently) with k8s. Container images are built via [GitHub Actions](/.github/workflows/build.yml) and uploaded to GitHub Container Registry (this repo's GHCR). GH Actions also handles semantic versioning using commit message substrings, saving them as GitHub tags (eg. `v0.0.30):

- `#skip-ci`: Skips the workflow completely; Useful if changing things unrelated to code (eg. README)
- `#none`: Builds new image, but with no semver change (eg. `v1.2.3` -> `v.1.2.3`)
- `#patch`: Increments patch version (eg. `v1.2.3` -> `v.1.2.4`)
- `#minor`: Increments minor version (eg. `v1.2.3` -> `v.1.3.3`)
- `#major`: Increments major version (eg. `v1.2.3` -> `v.2.2.3`)
> If omitted, GHA will default to `none` (building image, no semver change).
When building the image, this semver is included in the image and can be safely used. The version in [package.json](./package.json) is not used.

### Local Development

To set things up:

```shell
# Clone the repository locally
git clone https://github.com/jgaribsin/helldivers2-bot.git
cd helldivers2-bot
# Install dependencies
npm install
```

To run the bot locally (or when deploying), you'll need some env vars:

## Privacy Policy
- `BOT_TOKEN` - Discord bot token, gotten from [Discord's developer portal](https://discord.com/developers/applications) (for more info, see [their docs](https://discord.com/developers/docs/intro))
- `DATABASE_URL` - PostgreSQL database connection string
- `NEW_RELIC_APP_NAME` - Display name for New Relic logs
- `NEW_RELIC_LICENSE_KEY` - New Relic API Key (type: INGEST-LICENSE)
- `NODE_ENV` - "development" or "production"

Running the application (watch mode)

```shell
npm run watch
```

You can view the Database locally via [Drizzle Studio](https://orm.drizzle.team/drizzle-studio/overview). Ensure `DATABASE_URL` is set up, per above.

```shell
npx drizzle-kit studio
```

Building the application locally, or a Docker image:

```shell
# Build locally
npm run build
# Build Docker image
docker build . -t hellcom:latest
```

More documentation to come as I remember/verify things.

> Thanks for reading, have an awesome day!
Binary file removed helldivers_logo.jpg
Binary file not shown.
Binary file added images/helldiver_bot_logo_alt_transparent.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/helldiver_logo_white_ring.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/helldiver_logo_white_ring_transparent.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/helldiver_logo_yellow_ring.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"name": "helldivers-bot",
"description": "Welcome to the repo for Helldivers's discord bot! This bot is intended to be a helper for Helldivers's team and community.",
"name": "hellcom-bot",
"description": "Welcome to the repo for HellCom Discord bot! This bot is intended to be a helper/utility for Helldivers's community. Democracy!",
"version": "0.0.0",
"author": "jgaribsin",
"license": "ISC",
"license": "AGPL-3.0-only",
"scripts": {
"lint": "gts lint",
"clean": "gts clean",
Expand Down
2 changes: 1 addition & 1 deletion scripts/strip_db_data.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {db, apiData, eq} from '../src/db';
import {db} from '../src/db';
import fs from 'fs';

const main = async () => {
Expand Down
21 changes: 1 addition & 20 deletions src/commands/discord.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {EmbedBuilder, SlashCommandBuilder} from 'discord.js';
import {Command} from '../interfaces';
import {EMBED_COLOUR, FOOTER_MESSAGE} from './_components';
import {config, helldiversConfig} from '../config';
import {config} from '../config';
import {client} from '../handlers';

const {
Expand All @@ -11,7 +11,6 @@ const {
TOP_GG_LINK,
BOT_OWNER,
} = config;
const {icons} = helldiversConfig;

const command: Command = {
data: new SlashCommandBuilder()
Expand All @@ -28,24 +27,6 @@ const command: Command = {
iconURL: owner?.avatarURL?.() || undefined,
})
.setTitle('Leviathan Alliance - Helldivers 2 Community')
// .setDescription(
// 'HellCom is part of **Leviathan Alliance**! ' +
// '\n\n' +
// 'Leviathan Alliance is a community of welcoming, like-minded Helldivers who are passionate about Helldivers 2! We also built this server knowing that the official Helldivers Discord can be hectic and overwhelming, so we hope you can find friendly Helldivers more easily here!' +
// '\n\n' +
// 'You may also see patches for the bot, report bugs or give suggestions __for the bot__. ' +
// 'If you would like to invite the bot to your own server, you may do so with ' +
// `__**[this link (click)](${DISCORD_APPLICATION_DIRECTORY})**__ (you must be a server admin).` +
// '\n\n' +
// ' HellCom is a personal project, worked on in my spare time. ' +
// `If you'd like to help cover hosting costs, or just support me in general, you can with my **[ko-fi link](${KOFI_LINK})**.` +
// '\n' +
// `Another way to support HellCom is via [voting for and/or reviewing it on top.gg](${TOP_GG_LINK}) so fellow Helldivers can find it more easily!` +
// '\n\n' +
// `HellCom is a community-driven project, so I'm always open to suggestions and bug reports. Feel free to join the **[Discord](${DISCORD_INVITE})** and chat with me there!` +
// '\n\n' +
// 'Thanks for your interest in the project! <3'
// )
.setFields(
{
name: 'HellCom is part of Leviathan Alliance!',
Expand Down
6 changes: 1 addition & 5 deletions src/commands/dispatches.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
import {
CommandInteraction,
EmbedBuilder,
SlashCommandBuilder,
} from 'discord.js';
import {EmbedBuilder, SlashCommandBuilder} from 'discord.js';
import {Command} from '../interfaces';
import {FOOTER_MESSAGE} from './_components';
import {getAllDispatches} from '../api-wrapper';
Expand Down
18 changes: 1 addition & 17 deletions src/commands/history.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
SlashCommandBuilder,
} from 'discord.js';
import {Command} from '../interfaces';
import {EMBED_COLOUR, FACTION_COLOUR, FOOTER_MESSAGE} from './_components';
import {FOOTER_MESSAGE} from './_components';
import {apiData, db} from '../db';
import {desc} from 'drizzle-orm';
import {ChartConfiguration} from 'chart.js';
Expand All @@ -30,10 +30,7 @@ const command: Command = {
};

const subcmds: {[key: string]: (job: CommandInteraction) => Promise<void>} = {
// hashmap of subcommands
// TODO:
players,
sub2,
};

async function players(interaction: CommandInteraction) {
Expand Down Expand Up @@ -144,17 +141,4 @@ async function players(interaction: CommandInteraction) {
await interaction.editReply({embeds: [embed], files: [attachment]});
}

async function sub2(interaction: CommandInteraction) {
// TODO: implement subcommand
const embed = new EmbedBuilder()
.setTitle('EMBED_TITLE')
.setDescription('EMBED_DESC')
.setFooter({text: FOOTER_MESSAGE})
.setTimestamp();

// we use editReply because slashcommands are deferred by default
// discord requires a response within 3 seconds, so we defer a response and then edit it later
await interaction.editReply({embeds: [embed]});
}

export default command;
13 changes: 2 additions & 11 deletions src/commands/planet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,9 @@ import {
EmbedBuilder,
SlashCommandBuilder,
} from 'discord.js';
import {
getAllActivePlanets,
getPlanetAttacks,
getPlanetByName,
getAllPlayers,
getAllCampaigns,
} from '../api-wrapper';
import {getPlanetByName, getAllCampaigns} from '../api-wrapper';
import {Command} from '../interfaces';
import {EMBED_COLOUR, FACTION_COLOUR, FOOTER_MESSAGE} from './_components';
import {FACTION_COLOUR, FOOTER_MESSAGE} from './_components';
import {planetNameTransform, warStatusEmbeds} from '../handlers';

const command: Command = {
Expand Down Expand Up @@ -70,11 +64,8 @@ async function info(interaction: CommandInteraction) {
}

const {
index,
name,
sector,
maxHealth,
initialOwner,
owner,
health,
lossPercPerHour,
Expand Down
13 changes: 0 additions & 13 deletions src/commands/subscribe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ import {Command} from '../interfaces';
import {EMBED_COLOUR, FOOTER_MESSAGE} from './_components';
import {
client,
missingChannelPerms,
sleep,
subscribeEmbed,
subscribeNotifEmbed,
warStatusPersistentMessage,
Expand Down Expand Up @@ -69,17 +67,6 @@ const command: Command = {
run: async interaction => {
const subcommand = interaction.options.data[0].name;

// TODO: re-enable one the bot is approved for priv intents
// if (interaction.guild) {
// const user = await interaction.guild.members.fetch(interaction.user.id);
// if (!user.permissions.has('ManageMessages')) {
// // respond with missing perms, then delete the response after 5s
// await interaction.editReply(missingChannelPerms(interaction));

// return;
// }
// }

await subcmds[subcommand](interaction);
},
};
Expand Down
1 change: 0 additions & 1 deletion src/config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import 'dotenv/config';
require('newrelic');
import {version} from '../package.json';
import {Faction} from './api-wrapper';
const isProd = process.env.NODE_ENV === 'production';

const configObj: Record<string, string | number | undefined> = {
Expand Down
2 changes: 0 additions & 2 deletions src/db/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ import {
json,
pgEnum,
pgTable,
serial,
timestamp,
uniqueIndex,
varchar,
} from 'drizzle-orm/pg-core';
import {StrippedApiData} from '../api-wrapper';
Expand Down
27 changes: 4 additions & 23 deletions src/handlers/embed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ export function subscribeNotifEmbed(type: string): EmbedBuilder[] {
}

export function majorOrderEmbed(assignment: Assignment) {
const {expiresIn, id32, progress, setting} = assignment;
const {expiresIn, progress, setting} = assignment;
const {
type: settingsType,
overrideTitle,
Expand Down Expand Up @@ -352,7 +352,7 @@ export async function campaignEmbeds(planet_name?: string) {

const embeds = [];
for (const campaign of campaigns) {
const {planetName, type, campaignType, planetData, planetEvent} = campaign;
const {planetName, campaignType, planetData, planetEvent} = campaign;
const title = `${planetName}: ${campaignType.toUpperCase()}`;
const planetThumbnailUrl = `https://helldiverscompanionimagescdn.b-cdn.net/planet-images/${planetNameTransform(
planetName
Expand All @@ -364,7 +364,6 @@ export async function campaignEmbeds(planet_name?: string) {
initialOwner,
owner,
health,
regenPerSecond,
players,
playerPerc,
liberation,
Expand All @@ -390,15 +389,8 @@ export async function campaignEmbeds(planet_name?: string) {
embed.addFields({name: key, value: val.toString(), inline: true});
}
} else if (campaignType === 'Defend') {
const {
maxHealth,
health,
defence,
eventType,
race,
startTime,
expireTime,
} = planetEvent as MergedPlanetEventData;
const {maxHealth, health, defence, race, expireTime} =
planetEvent as MergedPlanetEventData;
const {players, playerPerc, owner} = planetData;

const embed = new EmbedBuilder()
Expand Down Expand Up @@ -426,17 +418,6 @@ export async function campaignEmbeds(planet_name?: string) {
return embeds;
}

function drawLoadingBar(total: number, current: number, barLength: number) {
const percentage = current / total;
const progress = Math.round(barLength * percentage);
const empty = barLength - progress;

const progressBar = '[`' + '#'.repeat(progress) + ' '.repeat(empty) + '`]';
const percentageText = (percentage * 100).toFixed(2) + '%';

return progressBar + ' ' + percentageText;
}

function drawLoadingBarPerc(percentage: number, barLength: number) {
const percMult = percentage / 100;
const progress = Math.round(barLength * percMult);
Expand Down
2 changes: 1 addition & 1 deletion src/handlers/graphs.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {ChartConfiguration} from 'chart.js';
import {ChartJSNodeCanvas} from 'chartjs-node-canvas';
import {apiData, db} from '../db';
import {asc, desc} from 'drizzle-orm';
import {desc} from 'drizzle-orm';
import {MergedCampaignData, getCampaignByPlanetName} from '../api-wrapper';
import {dayjs} from './dates';

Expand Down
2 changes: 1 addition & 1 deletion src/handlers/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const SUBSCRIBE_FOOTER = config.SUBSCRIBE_FOOTER;

export async function updateMessages() {
// measure time taken to update all persistent messages
const start = Date.now();
// const start = Date.now();

const embeds = {
curr_war: await warStatusPersistentMessage(),
Expand Down
12 changes: 0 additions & 12 deletions todo.md

This file was deleted.

0 comments on commit a41466b

Please sign in to comment.