Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Discord Bot #417

Open
wants to merge 22 commits into
base: master
Choose a base branch
from
Open

Discord Bot #417

wants to merge 22 commits into from

Conversation

Argn0
Copy link
Contributor

@Argn0 Argn0 commented Feb 25, 2022

All roles that have previously been set manually will be removed. Users who are still subscribers must assign them again themselves.

closes #63


try {
// get all known users who have been assigned a sub role
const result = await postgres?.query('SELECT * FROM discord');
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with this being one-way (e.g. we don't remove roles for former subscribers).

@Argn0
Copy link
Contributor Author

Argn0 commented Feb 25, 2022

This must be set for the bot to work

discord bot

server/server.ts Outdated
return res.status(400).json({ error: 'invalid user token' });
}
const customer = await getCustomerByEmail(decoded.email as string);
const isSubscriber = Boolean(
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have a helper function for this logic?

server/server.ts Outdated
const isSubscriber = Boolean(
customer?.subscriptions?.data?.find((sub) => sub?.status === 'active')
);
if (!isSubscriber) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TBH I think it would be okay to let all users link a Discord, but then have the background process do the sub checking/role assigning

server/server.ts Outdated
true
);
if (user) {
await postgres?.query('DELETE FROM discord WHERE "customerId" = $1', [
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could just store email + discord id + sub state? And we could update the sub state in the same code that already syncs subs to postgres

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But do we have to save anything if we never remove the sub roles like you suggested above?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume users might want to delete/edit their discord linked account

server/server.ts Outdated
}
return res.status(200).json({});
})
.get('/discordRole', async (req, res) => {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we return this in the metadata call maybe?

});
};

setDiscordUsername(value: string) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rather than making the user enter a name and risk errors (or entering someone else's name), could we integrate with Discord OAuth to automatically fetch the user's info?

@Argn0
Copy link
Contributor Author

Argn0 commented Feb 26, 2022

In the generated authentication URL the response_type=code must be changed to response_type=token

@Argn0
Copy link
Contributor Author

Argn0 commented Feb 26, 2022

We don't do any security checks and right now someone could theoretically link someone else's Discord account but I don't believe this is something to be concerned about because no damage could be caused.

server/server.ts Outdated
try {
// users should only have one account max. with a sub role per email,
// set email to null to tell background process to clean it up.
await postgres?.query('UPDATE discord SET email = null WHERE email = $1', [
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wonder if it's better to have one links table that can support linking different types of accounts in the future by adding more columns?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean a table which has a column for each service's user ID? I think with a table like that we would not have to be matching the email address anymore.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we would still be matching the email to find the row, but that row could support multiple services.

server/server.ts Outdated Show resolved Hide resolved
const members = await guild?.members.fetch();

// from discord API get username and discriminator of all users on discord server without a sub role
const membersWithoutRole = members
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if we just joined the subscriber and discord links table and applied the role to all those users? Since the discord has thousands of members, might be more efficient? As mentioned before I don't think cleanup is essential and we can add a separate process for that later if needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we could do that. But we will have to add a hasRole column to the discord table so we don't keep trying to assign a role to users who already have one over and over again.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this may be moot if we just do the role add on link

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but to answer the question, can we check-then-assign for each user?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we can check role status for each user with an individual discord API call, if this is what you're asking.


deleteDiscord = async () => {
const token = await this.props.user.getIdToken();
await window.fetch(serverPath + '/discord/auth', {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems confusing to have the same POST call do delete. Maybe make a separate DELETE verb call to the same endpoint?

@Argn0
Copy link
Contributor Author

Argn0 commented Feb 27, 2022

The important settings are

  • SERVERMEMBERS INTENT toggled on in bot settings
  • In the generated authentication URL the response_type=code must be changed to response_type=token
  • Bot Permissions: Manage roles
  • OAuth2 URL scope : identify

return res.sendStatus(500);
}
try {
const result = await postgres?.query(
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think you can delete and return the deleted content?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it returns the deleted rows. The rows array is always empty. Are you sure it does? Maybe I'm missing a query method argument?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you need to add RETURNING to your query

server/server.ts Outdated
@@ -278,6 +384,19 @@ app.delete('/deleteAccount', async (req, res) => {
}
if (postgres) {
await postgres?.query('DELETE FROM room WHERE owner = $1', [decoded.uid]);
const result = await postgres?.query(
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can just delete here and not change role?

server/server.ts Outdated
[decoded.email]
);
if (result.rowCount) {
await discordBot.assignRole(
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the link action? we don't need to remove any roles?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you pass the argument undo=true to assignRole it removes the role. Maybe this is a bit confusing?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah i'm asking why we need to remove the role here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I see. Ok we will leave out all role removing stuff.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To answer the question: We remove the role here because we want to limit the linked discord accounts per email to max 1 . if we dont remove the role here someone could theoretically assign a sub role to an unlimited number of accounts.

Would that be fine?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But we defer this cleanup to later, right?

sql/schema.sql Outdated
@@ -25,6 +25,13 @@ CREATE TABLE subscriber(
PRIMARY KEY(uid)
);

CREATE TABLE discord(
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any more thoughts on making this a generic link table that can support linking other accounts?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds finde to me. Do you have any naming suggestion for the table?

@@ -109,11 +116,16 @@ class WatchParty extends React.Component {
);
}}
/>
<Route path="/discord/auth" exact>
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make this a modal on the app rather than its own route? or is a new path required for the discord auth?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The user will redirected to the URL you choose when generating the OAuth2 URL. In our case the user will be redirected to watchparty.me/discord/auth . So I think we need this new path.

@Argn0
Copy link
Contributor Author

Argn0 commented Feb 27, 2022

Alright, I believe I've addressed everything.

@Argn0
Copy link
Contributor Author

Argn0 commented Mar 1, 2022

I tried doing this in a modal instead of a new window but Discord is blocking iframes and I don't know if there is another method.

onClick={this.authDiscord}
>
<Icon name="discord" />
Link Discord Account
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's say "Link Discord Account/Get Subscriber Role"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That looked a little awkward because it's too long. I've added a popup instead.

@@ -57,6 +59,23 @@ if (config.DATABASE_URL) {
postgres.connect();
}

let discordBot: DiscordBot;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably run in a separate service

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, that might make the endpoint logic more complicated. But we may need to scale to multiple instances of the server soon, and I'm not sure if that will work if multiple processes are logging into the same Discord bot

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think would be the best way to do the communication between the server processes and the discord process?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we could just use a redis queue?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But that might not be necessary because as far as I can tell it is possible to access the same bot through multiple different client instances using the same token.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My preferred solution is usually to set up an HTTP server on the bot and have the web instances call it. But if you think it might be OK to scale out, then we can try that first

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried it out by starting the dev server multiple times. Which I guess comes close to having multiple instances of the same server.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

add self-service link to discord account for subscribers
2 participants