Skip to content

Commit

Permalink
Merge pull request #55 from MinaFoundation/feature/mef-login-in-forum
Browse files Browse the repository at this point in the history
Feature/mef login in forum
  • Loading branch information
iluxonchik authored Nov 27, 2024
2 parents e47d8fa + 0a9deac commit 0d5e8df
Show file tree
Hide file tree
Showing 11 changed files with 501 additions and 25 deletions.
93 changes: 75 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,33 @@ See documentation on the plan for this [here](https://docs.google.com/document/d
3. Create a `.env` file in the root directory with the following variables:

```
# Discord Bot Configuration
DISCORD_TOKEN=your_discord_bot_token
CLIENT_ID=your_client_id (A.K.A. application id)
GUILD_ID=your_guild_id (A.K.A. server id)
PUBLIC_KEY=your_apps_public_key
# JWT Configuration for Login
JWT_PRIVATE_KEY_RS512=your_private_key
JWT_PUBLIC_KEY_RS512=your_public_key
# The keys should be in PEM format and include the BEGIN/END headers
# MEF Connection Configuration
MEF_FE_BASE_URL=http://your-mef-instance-url
# The URL where your MEF instance is hosted. This is used for generating login links.
# For staging environment, use: http://pgt-staging.minaprotocol.network
# Login Forum Configuration
LOGIN_FORUM_CHANNEL_ID=your_forum_channel_id
# The Discord forum channel ID where the "Login to MEF" interface will be displayed.
# If not set, defaults to: 1301096522452303894
# The bot will create a thread titled "Login to MEF" in this forum channel,
# containing a button that users can click to receive a secure, 30-second login link.
# Logging Configuration
GSS_LOG_LEVEL=DEBUG
# Available values: ERROR, WARN, INFO, DEBUG
# Set to DEBUG for detailed logging during development
```

4. Run `npm run dev` to start the bot in development mode
Expand All @@ -47,33 +70,67 @@ In order to add an admin to the bot, you can run the following command:
npm run manage addAdmin [discord_user_id]
```

## Contributions
## Container Support

To make a contribution, follow these steps:
The bot can be run using either Docker or Podman. Both methods use the same configuration files.

1. Make an issue that includes a user story for what the user should be able to do.
2. Get that issue tested by: es92, Remigiusz-antczak or Cristina Echeverry.
3. Get that issue approved by the product owners: es92, Remigiusz-antczak or Cristina Echeverry.
4. Write a PR and get it approved by the code owners and Mina devops: Es92, illya (developer), johnmarcou (Mina devops). Each PR must correspond to an approved issue. By default, PRs should be merged by the PR submitter, though in some cases if changes are needed, they can be merged by code owners.
### Using Docker

## Docker image
Build and run:
```bash
# Build the image
docker build -t govbot:latest .

### Build it
# Run with environment variables
docker run -it -d --env-file ./.env govbot:latest

```
docker build -t <your-image-name>:<your-image-tag> .
```
# Run with docker-compose
docker-compose up --build -d

### Run it
# View logs
docker logs -f pgt_govbot_bot_1

```
docker run -it -d --env-file ./.env <your-image-name>:<your-image-tag>
# Stop and remove containers
docker-compose down
```

The `--env-file` flag takes a filename as an argument and expects each line to be in the VAR=VAL format, mimicking the argument passed to `--env`. Comment lines need only be prefixed with #
### Using Podman

### Test it
Build and run:
```bash
# Build and start with podman-compose
podman-compose -f docker-compose.yaml up --build -d

# View logs
podman logs -f pgt_govbot_bot_1

# Stop and remove containers
podman-compose -f docker-compose.yaml down
```
docker-compose up --build
```

Note: The docker-compose.yaml file is compatible with both Docker and Podman.

## Login to MEF Functionality

The bot provides a "Login to MEF" interface in a designated forum channel. Here's how it works:

1. The bot creates a thread titled "Login to MEF" in the specified forum channel
2. The thread contains a button that users can click to receive a secure login link
3. Login links are ephemeral (only visible to the requesting user) and expire after 30 seconds
4. The forum channel can be configured using the `LOGIN_FORUM_CHANNEL_ID` environment variable
5. Login tokens are signed using RS512 algorithm with the configured JWT keys

To change the forum channel:
1. Create a new forum channel in your Discord server
2. Copy the channel ID (Right-click > Copy ID)
3. Set `LOGIN_FORUM_CHANNEL_ID` in your .env file to the new channel ID
4. Restart the bot

## Contributions

To make a contribution, follow these steps:

1. Make an issue that includes a user story for what the user should be able to do.
2. Get that issue tested by: es92, Remigiusz-antczak or Cristina Echeverry.
3. Get that issue approved by the product owners: es92, Remigiusz-antczak or Cristina Echeverry.
4. Write a PR and get it approved by the code owners and Mina devops: Es92, illya (developer), johnmarcou (Mina devops). Each PR must correspond to an approved issue. By default, PRs should be merged by the PR submitter, though in some cases if changes are needed, they can be merged by code owners.
77 changes: 77 additions & 0 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

34 changes: 34 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
description = "Next.js development environment with Podman";

inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
# Systems support
systems.url = "github:nix-systems/default";
};

outputs = { self, nixpkgs, systems, flake-utils, ... }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
in
{
devShells.default = pkgs.mkShell {
buildInputs = with pkgs; [
# Core dependencies
nodejs_20
nodePackages.typescript
podman
podman-compose
direnv
git
];

# Add environment variables
env = {
NODE_ENV = "production";
};
};
});
}
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "mina-govbot",
"version": "0.0.22",
"version": "0.0.23",
"description": "Discord bot for collective decision making for Mina Protocol",
"main": "index.js",
"directories": {
Expand Down
25 changes: 23 additions & 2 deletions src/bot.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CacheType, Client, GatewayIntentBits, Interaction, TextChannel } from 'discord.js';
import { CacheType, Client, GatewayIntentBits, Interaction, TextChannel, ChannelType } from 'discord.js';
import { config } from 'dotenv';
import { DashboardManager } from './core/DashboardManager';
import { AdminDashboard } from './channels/admin/dashboard';
Expand All @@ -20,6 +20,9 @@ import { DiscordStatus } from './channels/DiscordStatus';
import { TrackedInteraction } from './core/BaseClasses';
import { networkInterfaces } from 'os';
import { startServer } from './app';
import { LoginDashboard } from './channels/login/LoginDashboard';
import { LoginScreen } from './channels/login/screens/LoginScreen';
import { LoginForumManager } from './channels/login/LoginForumManager';

config();

Expand Down Expand Up @@ -69,8 +72,26 @@ client.once('ready', async () => {
considerDashboard.homeScreen = considerHomeScreen;
dashboardManager.registerDashboard('consider', considerDashboard);

// Render initial screen in #admin channel
// Initialize login dashboard
const loginDashboard = new LoginDashboard();
const loginScreen = new LoginScreen(loginDashboard, LoginScreen.ID);
loginDashboard.homeScreen = loginScreen;
dashboardManager.registerDashboard('login', loginDashboard);

// Initialize login forum through renderToTextChannel
const guild = client.guilds.cache.first();
if (guild) {
const loginForumId = process.env.LOGIN_FORUM_CHANNEL_ID || LoginScreen.DEFAULT_FORUM_CHANNEL_ID;
const loginForumChannel = await guild.channels.fetch(loginForumId);
if (loginForumChannel && loginForumChannel.type === ChannelType.GuildForum) {
// We don't actually need to call renderToTextChannel since we're dealing with a forum
await LoginForumManager.initialize(loginForumId, loginScreen);
} else {
logger.error('Login forum channel not found or is not a forum channel');
}
}

// Render initial screen in #admin channel
if (guild) {
const adminChannel = guild.channels.cache.find((channel) => channel.name === 'admin') as TextChannel | undefined;
if (adminChannel) {
Expand Down
31 changes: 31 additions & 0 deletions src/channels/login/LoginDashboard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Dashboard, TrackedInteraction } from '../../core/BaseClasses';
import { client } from '../../bot';
import logger from '../../logging';
import { ChannelType, ThreadChannel } from 'discord.js';
import { CustomIDOracle } from '../../CustomIDOracle';

export class LoginDashboard extends Dashboard {
public static readonly ID = 'login';
public static readonly DEFAULT_FORUM_CHANNEL_ID = '1301096522452303894';

constructor() {
super(LoginDashboard.ID);
}

public async isFallback(interaction: TrackedInteraction): Promise<boolean> {
// First check if this interaction has a customId (button clicks do)
if (!interaction.customId) {
return false;
}

// Get the dashboard ID from the customId (which is the forum channel ID)
const forumChannelId = CustomIDOracle.getDashboardId(interaction.customId);
if (!forumChannelId) {
return false;
}

// Check if this is our login forum channel
const loginForumId = process.env.LOGIN_FORUM_CHANNEL_ID || LoginDashboard.DEFAULT_FORUM_CHANNEL_ID;
return forumChannelId === loginForumId;
}
}
Loading

0 comments on commit 0d5e8df

Please sign in to comment.