From a124e3ef9ddec6e0fd401cc65c91cda1d926cc53 Mon Sep 17 00:00:00 2001 From: karntrehan Date: Tue, 1 Oct 2024 16:52:14 +0530 Subject: [PATCH 1/3] Fix user auth flow. --- .env.sample | 22 ++++----- cogs/userInteractions.py | 10 +++-- helpers/supabaseClient.py | 6 ++- main.py | 94 +++++++++++++++------------------------ 4 files changed, 59 insertions(+), 73 deletions(-) diff --git a/.env.sample b/.env.sample index 1a8a62d..26f9f02 100644 --- a/.env.sample +++ b/.env.sample @@ -1,13 +1,15 @@ -#token to connect to the bot -TOKEN="" -#discord server id -SERVER_ID="" -INTRODUCTIONS_CHANNEL="" -NON_CONTRIBUTOR_ROLES="" +# Bot Token: +TOKEN= +SERVER_ID= +INTRODUCTIONS_CHANNEL= +NON_CONTRIBUTOR_ROLES= -FLASK_HOST="" +FLASK_HOST= +GITHUB_PAT= +WEBHOOK_URL= -SUPABASE_URL="" -SUPABASE_KEY="" +GITHUB_AUTHENTICATION_URL= -GithubPAT="" +#SUPABASE_URL= +#SUPABASE_KEY= +#VERIFIED_ROLE_ID= \ No newline at end of file diff --git a/cogs/userInteractions.py b/cogs/userInteractions.py index 3966368..476edba 100644 --- a/cogs/userInteractions.py +++ b/cogs/userInteractions.py @@ -3,9 +3,12 @@ import discord from discord.ext import commands, tasks +from dotenv import find_dotenv, load_dotenv from helpers.supabaseClient import SupabaseClient +load_dotenv(find_dotenv()) + VERIFIED_CONTRIBUTOR_ROLE_ID = 1247854311191351307 NON_CONTRIBUTOR_ROLES = [973852321870118914, 976345770477387788, 973852439054782464] NON_CONTRIBUTOR_MEMBERS = [ @@ -165,10 +168,11 @@ class AuthenticationView(discord.ui.View): def __init__(self, discord_userdata): super().__init__() self.timeout = None + github_auth_url = os.getenv("GITHUB_AUTHENTICATION_URL") button = discord.ui.Button( label="Authenticate Github", style=discord.ButtonStyle.url, - url=f"https://backend.c4gt.samagra.io/authenticate/{discord_userdata}", + url=f"{github_auth_url}/{discord_userdata}", ) self.add_item(button) self.message = None @@ -207,12 +211,12 @@ async def list_badges(self, ctx): async def update_contributors(self): print("update_contributors running") contributors = SupabaseClient().read_all("contributors_registration") - print("Contributor lenght: "+len(contributors)) + print("Contributor length: ", len(contributors)) guild = await self.bot.fetch_guild(os.getenv("SERVER_ID")) contributor_role = guild.get_role(VERIFIED_CONTRIBUTOR_ROLE_ID) count = 1 for contributor in contributors: - print("Member count: "+count) + print("Member count: ", count) count += 1 member = guild.get_member(contributor["discord_id"]) if member and contributor_role not in member.roles: diff --git a/helpers/supabaseClient.py b/helpers/supabaseClient.py index 47e3441..595cccc 100644 --- a/helpers/supabaseClient.py +++ b/helpers/supabaseClient.py @@ -1,10 +1,12 @@ import os from discord import Member, User +import dotenv from supabase import Client, create_client from helpers.roleHelpers import lookForRoles +dotenv.load_dotenv(".env") class SupabaseClient: def __init__(self, url=None, key=None) -> None: @@ -63,7 +65,7 @@ def read_by_order_limit( def read_all(self, table): data = self.client.table(table).select("*").execute() return data.data - + def read_all_active(self, table): data = self.client.table(table).select("*").eq('is_active', 'true').execute() return data.data @@ -161,4 +163,4 @@ def deleteContributorDiscord(self, contributorDiscordIds): def invalidateContributorDiscord(self, contributorDiscordIds): table = "contributors_discord" for id in contributorDiscordIds: - self.client.table(table).update({ 'is_active': 'false' }).eq('discord_id', id).execute() + self.client.table(table).update({'is_active': 'false'}).eq('discord_id', id).execute() diff --git a/main.py b/main.py index 156ce07..10c851f 100644 --- a/main.py +++ b/main.py @@ -1,31 +1,31 @@ import asyncio -import json import os import sys from typing import Union -import aiohttp import discord -import dotenv from discord.ext import commands from cogs.vcCog import VCProgramSelection from helpers.supabaseClient import SupabaseClient +from dotenv import load_dotenv, find_dotenv + # Since there are user defined packages, adding current directory to python path current_directory = os.getcwd() sys.path.append(current_directory) -dotenv.load_dotenv(".env") +load_dotenv(find_dotenv()) class AuthenticationView(discord.ui.View): def __init__(self, discord_userdata): super().__init__() + github_auth_url = os.getenv("GITHUB_AUTHENTICATION_URL") button = discord.ui.Button( label="Authenticate Github", style=discord.ButtonStyle.url, - url=f"https://backend.c4gt.samagra.io/authenticate/{discord_userdata}", + url=f"{github_auth_url}/{discord_userdata}", ) self.add_item(button) self.message = None @@ -41,28 +41,6 @@ def __init__( ) -> None: super().__init__(title=title, timeout=timeout, custom_id=custom_id) - async def post_data(self, table_name, data): - print("PostData: "+table_name) - print("PostData: "+data) - url = f"{os.getenv('SUPABASE_URL')}/rest/v1/{table_name}", - print("PostData: "+url) - headers = { - "apikey": f"{os.getenv('SUPABASE_KEY')}", - "Authorization": f"Bearer {os.getenv('SUPABASE_KEY')}", - "Content-Type": "application/json", - "Prefer": "return=minimal", - } - - async with aiohttp.ClientSession() as session: - async with session.post( - url, headers=headers, data=json.dumps(data) - ) as response: - if response.status == 200: - print("Data posted successfully") - else: - print("Failed to post data") - print("Status Code:", response.status) - name = discord.ui.TextInput( label="Please Enter Your Name", placeholder="To give you the recognition you deserve, could you please share your full name for the certificates!", @@ -74,61 +52,61 @@ async def post_data(self, table_name, data): ) async def on_submit(self, interaction: discord.Interaction): - print("On_Submit") user = interaction.user - supaClient = SupabaseClient() - await interaction.response.send_message( - "Thanks! Now please sign in via Github!", - view=AuthenticationView(user.id), - ephemeral=True, - ) + + # Upsert user data to db user_data = { "name": self.name.value, "discord_id": user.id, "country": self.country.value } - await self.post_data("contributors_discord", user_data) + supaClient = SupabaseClient() + response = (supaClient.client.table("contributors_discord") + .upsert(user_data, on_conflict="discord_id").execute()) + print("DB updated for user:", response.data[0]["discord_id"]) - verifiedContributorRoleID = 1247854311191351307 - print("User:", type(user)) + verifiedContributorRoleID = int(os.getenv("VERIFIED_ROLE_ID")) if verifiedContributorRoleID in [role.id for role in user.roles]: - print("User already verified. Returning") - return + print("Already a verified contributor. Stopping.") + await interaction.response.send_message( + "Thanks! You are already a verified contributor on our server!", + ephemeral=True, + ) else: + await interaction.response.send_message( + "Thanks! Now please sign in via Github!.\n\n*Please Note: Post Github Authentication it may take upto 10 mins for you to be verified on this discord server. If there is a delay, please check back.*", + view=AuthenticationView(user.id), + ephemeral=True, + ) + async def hasIntroduced(): - print("Checking hasIntroduced...") - # try: - # print("Trying has authenticated") - # authentication = supaClient.read( - # "contributors_registration", "discord_id", user.id - # ) - # except Exception as e: - # print("Failed hasIntroduced: "+e) authentication = False - print("Authentication: "+authentication) while not authentication: - print("Not authenticated") - await asyncio.sleep(30) - print("Found!") + print("Not authenticated. Waiting") + await asyncio.sleep(15) + authentication = supaClient.read("contributors_registration", "discord_id", user.id) discordEngagement = supaClient.read( "discord_engagement", "contributor", user.id )[0] - print("Discord engagement: "+discordEngagement) + print("User has authenticated") return discordEngagement["has_introduced"] try: - print("Trying hasIntroduced") - await asyncio.wait_for(hasIntroduced(), timeout=1000) - print("Timedout on hasIntroduced") + await asyncio.wait_for(hasIntroduced(), timeout=300) verifiedContributorRole = user.guild.get_role(verifiedContributorRoleID) if verifiedContributorRole: - if verifiedContributorRole not in user.roles: + try: await user.add_roles( verifiedContributorRole, reason="Completed Auth and Introduction", ) + print("Added " + verifiedContributorRole.name + " role for: "+str(user.id)) + except Exception as e: + print(e) + else: + print("Verified contributor role not found with ID") except asyncio.TimeoutError: - print("Timed out waiting for authentication") + print("Timed out waiting for authentication for: "+str(user.id)) class RegistrationView(discord.ui.View): @@ -200,7 +178,7 @@ async def load(): async def main(): async with client: await load() - await client.start(os.getenv("TOKEN")) + await client.start(os.getenv("TESTING_TOKEN")) asyncio.run(main()) From 50a94de05b626cf2c9d3ebb3b6fa504e712c41f0 Mon Sep 17 00:00:00 2001 From: karntrehan Date: Tue, 1 Oct 2024 17:11:01 +0530 Subject: [PATCH 2/3] Fixing user update job as well. --- cogs/userInteractions.py | 208 +++++---------------------------------- main.py | 5 +- 2 files changed, 24 insertions(+), 189 deletions(-) diff --git a/cogs/userInteractions.py b/cogs/userInteractions.py index 476edba..5dd58ae 100644 --- a/cogs/userInteractions.py +++ b/cogs/userInteractions.py @@ -9,173 +9,7 @@ load_dotenv(find_dotenv()) -VERIFIED_CONTRIBUTOR_ROLE_ID = 1247854311191351307 -NON_CONTRIBUTOR_ROLES = [973852321870118914, 976345770477387788, 973852439054782464] -NON_CONTRIBUTOR_MEMBERS = [ - 704738663883472967, - 703157062825410590, - 697080616046559352, - 694445371816149062, - 677090546694619168, - 662243718354698240, - 637512076084117524, - 636277951540887553, - 599878601143222282, - 476285280811483140, - 459239263192612874, - 365127154847186945, - 314379504157982721, - 291548601228722177, - 280019116755124226, - 262810519184998400, - 222905396610859010, - 761623930531741788, - 760775460178755614, - 759107287322329128, - 753909213859938385, - 749287051035148328, - 730733764891770950, - 727567753246146633, - 722313444325457921, - 720297291105304627, - 712557512926298153, - 788670744120786976, - 805863967284920352, - 804299931543535626, - 902553914916896808, - 882206400074358835, - 1016564507394457663, - 1010140041789571072, - 1008331310806335618, - 989823092283035678, - 987081239892750376, - 986500267858083860, - 986245349129740338, - 973537784537186304, - 971297678401089556, - 969115972059406376, - 967973710617247796, - 965956645895147610, - 963733651529531444, - 961904402459947018, - 961529037610713098, - 961283715382775818, - 960435521786617856, - 948478097508954122, - 937569989689487420, - 936118598102028319, - 935911019828641812, - 933651897502535690, - 1087744252408246373, - 1086529617013252167, - 1083352549660307466, - 1079268207917027348, - 1077912548571095092, - 1075013295221768213, - 1070737923483389972, - 1059343450312544266, - 1052565902748553236, - 1050037458286420099, - 1049311176716206164, - 1045238281740230656, - 1044876122191581194, - 1044532981857001502, - 1043440759061352588, - 1042682119035568178, - 1039880103934570517, - 1037956974039535636, - 1036590822201757696, - 1024552723280052294, - 1018398460598308915, - 1108763601231171594, - 1108657717100429312, - 1108649642633199636, - 1108649434251792514, - 1108649344242040883, - 1108649032978538497, - 1108618174477369426, - 1108613175697481749, - 1108269782668681276, - 1107943353632432208, - 1107933295930511431, - 1107927044962144286, - 1107910689370165248, - 1107899551974686831, - 1107656943809593395, - 1107618486232039454, - 1107555866422562926, - 1107504062175391815, - 1101892125328679024, - 1100365706362626098, - 1099938102555975690, - 1098923182439796818, - 1093415042860466268, - 1091224095770816552, - 1120262010752471112, - 1115908663207530577, - 1115622606440239184, - 1115538129672224880, - 1115537984972931173, - 1115171977934688296, - 1114795277518389391, -] - - -class Announcement: - def __init__(self, member): - self.member = member - - async def create_embed(self): - embed = discord.Embed( - title=f"Hey {self.member.name}!", - description=f""" -If you submitted a proposal and did not make it to the C4GT mentoring program or you missed the deadline for applying, worry not! - -**We have launched the C4GT Community Program Today!**🚀 🚀 - -Through this program you can contribute to multiple projects, build your skills & get exclusive rewards & goodies. - -How will the Community Program work?🤔 -- **Explore Projects** 📋 - Explore [projects](https://www.codeforgovtech.in/community-projects) as per your skills, interest in the domain & more. -- **Get Coding** 💻 - Interact with mentors for clarity if required & solve the project -- **Points & Rewards** 🎁 - On each PR merged, you will get points. These points will give you badges & C4GT goodies. Read more about the point system [here](https://github.com/Code4GovTech/C4GT/wiki/Point-System-for-Contributors) - -How can you participate? -- **Link Discord & GitHub** 🤝 - Use this [link]({os.getenv('FLASK_HOST')}/authenticate/{self.member.id}) to connect these platforms, so we can track your activity & calculate points -- **Explore Issues Listed** 🖥️ - Keep an eye on our project page as more issues will be released every week. -- **Ask Questions** ❓ - Ask away your queries on the #c4gtcommunitychannel - -So what are you waiting for? Let's get started!!""", - color=0x00FFFF, - ) - - # embed.add_field(name="How will the Community Program work?🤔", - # value="- **Explore Projects** 📋 - Explore [projects](https://c4gt-ccbp-projects.vercel.app/) as per your skills, interest in the domain & more.\n- **Get Coding** 💻 - Interact with mentors for clarity if required & solve the project\n- **Points & Rewards** 🎁 - On each PR merged, you will get points. These points will give you badges & C4GT goodies. Read more about the point system [here](https://github.com/Code4GovTech/C4GT/wiki/Point-System-for-Contributors)") - - # embed.add_field(name="How can you participate?", - # value=f"- **Link Discord & GitHub** 🤝 - Use this [link]({os.getenv('''FLASK_HOST''')}/authenticate/{self.member.id}) to connect these platforms, so we can track your activity & calculate points\n- **Explore Issues Listed** 🖥️ - Keep an eye on our project page as more issues will be released every week.\n- **Ask Questions** ❓ - Ask away your queries on the #c4gtcommunitychannel") - # embed.add_field(name="So what are you waiting for? Let's get started!!", value='') - return embed - - -# This is a Discord View that is a set of UI elements that can be sent together in a message in discord. -# This view send a link to Github Auth through c4gt flask app in the form of a button. -class RegistrationModal(discord.ui.Modal, title="Contributor Registration"): - name = discord.ui.TextInput(label="Name") - - -class AuthenticationView(discord.ui.View): - def __init__(self, discord_userdata): - super().__init__() - self.timeout = None - github_auth_url = os.getenv("GITHUB_AUTHENTICATION_URL") - button = discord.ui.Button( - label="Authenticate Github", - style=discord.ButtonStyle.url, - url=f"{github_auth_url}/{discord_userdata}", - ) - self.add_item(button) - self.message = None +VERIFIED_CONTRIBUTOR_ROLE_ID = int(os.getenv("VERIFIED_ROLE_ID")) class UserHandler(commands.Cog): @@ -183,6 +17,27 @@ def __init__(self, bot) -> None: self.bot = bot self.update_contributors.start() + @tasks.loop(minutes=10) + async def update_contributors(self): + print("update_contributors running") + contributors = SupabaseClient().read_all("contributors_registration") + print("Contributors in DB: ", len(contributors)) + guild = await self.bot.fetch_guild(os.getenv("SERVER_ID")) + contributor_role = guild.get_role(VERIFIED_CONTRIBUTOR_ROLE_ID) + for contributor in contributors: + discord_id = contributor["discord_id"] + member = guild.get_member(discord_id) + if member: + if contributor_role not in member.roles: + await member.add_roles(contributor_role) + print(f"Given {contributor_role.name} Role to {member.name}") + else: + print(f"{member.name} is already {contributor_role.name}") + else: + print(f"{discord_id} is not a member on discord") + ## TODO delete from supabase as well? + return + @commands.command(aliases=["badges"]) async def list_badges(self, ctx): converseDesc = f"""Well done *{ctx.author.name}*! 👏 @@ -207,24 +62,6 @@ async def list_badges(self, ctx): return - @tasks.loop(minutes=10) - async def update_contributors(self): - print("update_contributors running") - contributors = SupabaseClient().read_all("contributors_registration") - print("Contributor length: ", len(contributors)) - guild = await self.bot.fetch_guild(os.getenv("SERVER_ID")) - contributor_role = guild.get_role(VERIFIED_CONTRIBUTOR_ROLE_ID) - count = 1 - for contributor in contributors: - print("Member count: ", count) - count += 1 - member = guild.get_member(contributor["discord_id"]) - if member and contributor_role not in member.roles: - # Give Contributor Role - await member.add_roles(contributor_role) - print(f"Given Roles to {member.name if member else 'None'}") - return - @commands.command() async def github_profile(self, ctx): if isinstance(ctx.channel, discord.DMChannel): @@ -280,7 +117,6 @@ async def github_profile(self, ctx): @update_contributors.before_loop async def before_update_loop(self): - print("starting auto-badge") await self.bot.wait_until_ready() async def read_members_csv(self, file_path): diff --git a/main.py b/main.py index 10c851f..9787b47 100644 --- a/main.py +++ b/main.py @@ -73,6 +73,7 @@ async def on_submit(self, interaction: discord.Interaction): ephemeral=True, ) else: + # User is not verified. Make them link with github and run polling to check when auth is done. await interaction.response.send_message( "Thanks! Now please sign in via Github!.\n\n*Please Note: Post Github Authentication it may take upto 10 mins for you to be verified on this discord server. If there is a delay, please check back.*", view=AuthenticationView(user.id), @@ -85,10 +86,8 @@ async def hasIntroduced(): print("Not authenticated. Waiting") await asyncio.sleep(15) authentication = supaClient.read("contributors_registration", "discord_id", user.id) - discordEngagement = supaClient.read( - "discord_engagement", "contributor", user.id - )[0] print("User has authenticated") + discordEngagement = supaClient.read("discord_engagement", "contributor", user.id)[0] return discordEngagement["has_introduced"] try: From 73b9251e4bb2697ed4b3c134a723326ff9010610 Mon Sep 17 00:00:00 2001 From: karntrehan Date: Tue, 1 Oct 2024 17:19:08 +0530 Subject: [PATCH 3/3] PR fixes --- .env.sample | 7 +++---- helpers/supabaseClient.py | 3 --- main.py | 2 +- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/.env.sample b/.env.sample index 26f9f02..d14ed4f 100644 --- a/.env.sample +++ b/.env.sample @@ -1,4 +1,3 @@ -# Bot Token: TOKEN= SERVER_ID= INTRODUCTIONS_CHANNEL= @@ -10,6 +9,6 @@ WEBHOOK_URL= GITHUB_AUTHENTICATION_URL= -#SUPABASE_URL= -#SUPABASE_KEY= -#VERIFIED_ROLE_ID= \ No newline at end of file +SUPABASE_URL= +SUPABASE_KEY= +VERIFIED_ROLE_ID= \ No newline at end of file diff --git a/helpers/supabaseClient.py b/helpers/supabaseClient.py index 595cccc..3200f21 100644 --- a/helpers/supabaseClient.py +++ b/helpers/supabaseClient.py @@ -1,13 +1,10 @@ import os from discord import Member, User -import dotenv from supabase import Client, create_client from helpers.roleHelpers import lookForRoles -dotenv.load_dotenv(".env") - class SupabaseClient: def __init__(self, url=None, key=None) -> None: self.supabase_url = url if url else os.getenv("SUPABASE_URL") diff --git a/main.py b/main.py index 9787b47..d4b20f1 100644 --- a/main.py +++ b/main.py @@ -177,7 +177,7 @@ async def load(): async def main(): async with client: await load() - await client.start(os.getenv("TESTING_TOKEN")) + await client.start(os.getenv("TOKEN")) asyncio.run(main())