Skip to content

Commit

Permalink
Add files via upload
Browse files Browse the repository at this point in the history
Main files.
  • Loading branch information
DionGR authored Jul 30, 2021
1 parent d6999ce commit b0d7b57
Show file tree
Hide file tree
Showing 9 changed files with 292 additions and 0 deletions.
47 changes: 47 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Discord Role Manager Bot

Role Manager is a discord BOT that utilizes Google Sheets for the organization of a server's hierarchy and permissions.


## Details

This BOT allows for you to export and organize your roles in a Google Sheet in a very user friendly way.

Three requests are made per usage of !export. One for clearing the spreadsheet, one for updating the roles/permission titles and one for the permission values themselves, making it extremely light and difficult for it to hit the Google Docs quota limit.
## Installation - Add the BOT to your server

Paste this on your browser to invite the BOT to a server you manage. The BOT requires the Administrator permission.

```bash
https://discord.com/api/oauth2/authorize?client_id=823014411945377824&permissions=8&scope=bot
```

## Usage - BOT Commands

```python
!setuphelp # Instructions on how to set up the BOT for your server.

!configure # Add your server to the BOT's database, along with your Google Sheet.

!export # Export your server's roles and their permissions to your Google Sheet.
```

## Requirements (for Developers)

```python
# Regular Module Imports
from os import path, sys
# Discord API Imports
import discord
from discord.ext import commands
# Google Docs/Sheets API Imports
import gspread
from oauth2client.service_account import ServiceAccountCredentials
from googleapiclient.discovery import build
```

## Contributing
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.

## License
[MIT](https://choosealicense.com/licenses/mit/)
12 changes: 12 additions & 0 deletions Role_Manager_Bot/credentials.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"type": "service_account",
"project_id": "python-role-management-bot",
"private_key_id": "PRIVATE_ID_HERE",
"private_key": "PRIVATE_KEY_HERE",
"client_email": "CLIENT_EMAIL_HERE",
"client_id": "ID_HERE",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "CLIENT_URL_HERE"
}
31 changes: 31 additions & 0 deletions Role_Manager_Bot/media.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"""
This file holds functions that return the link of the picture/link requested by the main function.
Mainly created to keep the main file clean from enormous links.
Instead of using a local file and uploading it everytime, it grabs the link.
"""


def picture(image_name=None):
images = {
"SUCCESS": "https://i.imgur.com/sp2zmN9.png",
"ERROR": "https://i.imgur.com/lLlHVPq.png",
"GSHEET": "https://i.imgur.com/u9PgNkk.png"
}
return images[image_name]


def color(color_type=None):
colors = {
"GREEN": 0x14F200,
"RED": 0xE10E0E
}
return colors[color_type]


def link(link_type=None):
links = {
"SPREADSHEET": "https://docs.google.com/spreadsheets/d/",
"SCOPE": ["https://spreadsheets.google.com/feeds", "https://www.googleapis.com/auth/spreadsheets", "https://www.googleapis.com/auth/drive.file", "https://www.googleapis.com/auth/drive"],
"TUTORIAL": "https://i.imgur.com/wG2DgC9.png"
}
return links[link_type]
Binary file added Role_Manager_Bot/media/ErrorLogo.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 Role_Manager_Bot/media/checkmark.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 Role_Manager_Bot/media/googlesheetslogo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
59 changes: 59 additions & 0 deletions Role_Manager_Bot/request_data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
"""
This file holds the functions that handle the requests' bodies,
as well as the data included in them, after they are processed and edited accordingly.
"""


def clear_request_body():
clear_data = {}
return clear_data


def titles_request_body(role_names, permission_names):
name_data = [
{
"majorDimension": "COLUMNS",
"range": "A2",
"values": [role_names]
}, # Fills the first column with all of the role names.
{
"majorDimension": "ROWS",
"range": "B1",
"values": [permission_names]
},
] # Fills the first column with all of the permission names.

request_body = {
"data": name_data,
"valueInputOption": "RAW"
}
return request_body


def values_request_body(permission_values):
permission_data = []
for i in range(len(permission_values)): # Creates a data list for the request in a smart way, so as to make the size of the request dynamic, depending on the amount of roles and permissions.
permission_data.append({"majorDimension": "ROWS", "range": "B" + str(i + 2), "values": [list(permission_values[i].values())]})

request_body = {
"data": permission_data,
"valueInputOption": "RAW"
}
return request_body


"""
Converts all the True/False values to ✔ and ❌ for user friendliness.
"""
def permission_values_to_emojis(permission_values, permission_names):
for i in range(len(permission_values)):
if permission_values[i]["administrator"]:
for perm_name in permission_names:
permission_values[i][perm_name] = "✔️"
else:
for perm_name in permission_names:
if permission_values[i][perm_name]:
permission_values[i][perm_name] = "✔️"
else:
permission_values[i][perm_name] = "❌"
return permission_values
142 changes: 142 additions & 0 deletions Role_Manager_Bot/role_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# Regular Module Imports
from os import path, sys
# Discord API Imports
import discord
from discord.ext import commands
# Google Docs/Sheets API Imports
import gspread
from oauth2client.service_account import ServiceAccountCredentials
from googleapiclient.discovery import build
# Assistance Files Imports
from media import *
from request_data import *

""" Google API Initializations """
SCOPES = link("SCOPE")
CREDENTIALS = ServiceAccountCredentials.from_json_keyfile_name(path.join(sys.path[0], "credentials.json"), SCOPES)
CLIENT = gspread.authorize(CREDENTIALS)
SERVICE = build("sheets", "v4", credentials=CREDENTIALS)

""" Discord API Initializations """
BOT = commands.Bot(command_prefix='!')


@BOT.event
async def on_ready():
print("BOT is ready and running!")


"""
!setuphelp - Admin Only
This command sends an embed with direct instructions on how
to get the Role Manager Bot set up and running on a server.
"""
@BOT.command()
@commands.has_permissions(administrator=True)
async def setuphelp(ctx):
embed = discord.Embed(title="Role Manager Setup Tutorial", description="Click the link above for detailed instructions with pictures!", url=link("TUTORIAL"), color=color("GREEN"))
embed.add_field(name="Step 1:", value="Create a Google Sheets Worksheet.", inline=False)
embed.add_field(name="Step 2:", value="Click the Share button on the top right and add this e-mail as an author: ```\n" + CREDENTIALS.service_account_email + "```", inline=False)
embed.add_field(name="Step 3:", value="Select 8 columns and right-click >> Insert 8 Columns in your worksheet.", inline=False)
embed.add_field(name="Step 4:", value="Run the !configure command and link your server with your Google spreadsheet.\n```!configure <WORKSHEET ID>```", inline=False)
embed.add_field(name="Step 5:", value="Export the role permissions onto the Google Sheet using:\n``` !export ```", inline=False)
embed.add_field(name="Finished!", value="The bot is now set up and you can start managing your roles! Make sure you provide a valid Spreadsheet ID, or you will encounter an error!", inline=False)
embed.set_thumbnail(url=picture("GSHEET"))
await ctx.send(embed=embed)


"""
!configure - Owner Only
This command creates a file for the server in the database (serverdata)
and stores the Google Worksheet ID inside a .txt file named after the server's ID.
If a file already exists, it prompts the user to update the file instead of reconfiguring it.
"""
@BOT.command()
@commands.has_permissions(administrator=True)
async def configure(ctx, *, spreadsheet_id=None):
if len(spreadsheet_id) == 44: # Ensure input was given and that it is valid.
if ctx.message.author.id == ctx.guild.owner_id: # If the sender is the server owner, proceed.
file_name = str(ctx.guild.id) + ".txt" # The name of the file is that of the server's unique ID.
try: # If the file exists, open and read it and give the link.
with open(path.join(path.dirname(path.realpath(__file__)) + r"\serverdata", file_name), "r+") as server_file:
server_file.truncate(0)
server_file.write(spreadsheet_id)

embed = discord.Embed(title="You already have a worksheet!", description="Your spreadsheet ID has been updated instead!", color=color("GREEN"))
embed.add_field(name="Your worksheet has been linked! Here's the link: ", value=link("SPREADSHEET") + spreadsheet_id)
embed.set_thumbnail(url=picture("GSHEET"))
await ctx.send(embed=embed)
except FileNotFoundError: # If it doesn't, create it and give the complete link.
with open(path.join(path.dirname(path.realpath(__file__)) + r"\serverdata", file_name), "w+") as server_file:
server_file.write(spreadsheet_id)

embed = discord.Embed(title="Worksheet Configuration Complete!", description="Your server has been added to the database.", color=color("GREEN"))
embed.add_field(name="Your worksheet has been linked! Here's the link: ", value=link("SPREADSHEET") + spreadsheet_id)
embed.set_thumbnail(url=picture("GSHEET"))
await ctx.send(embed=embed)
except Exception as exception:
print("Server ID:" + ctx.guild.id + "\n Exception:" + str(exception))
embed = discord.Embed(title="Something went wrong!", description="Please contact the BOT owner on GitHub!", color=color("RED"))
embed.add_field(name="Error code: ", value=str(exception))
embed.set_thumbnail(url=picture("ERROR"))
await ctx.send(embed=embed)
else: # If the sender is a simple Admin, refuse permission with an error embed.
embed = discord.Embed(title="Access Denied!", description="You have no proper authorization for this command.", color=color("RED"))
embed.add_field(name="This command may only be used by the server owner! ", value='<@' + str(ctx.guild.owner_id) + '>')
embed.set_thumbnail(url=picture("ERROR"))
await ctx.send(embed=embed)
else: # If no valid ID was given, ask for a valid ID and show instructions.
embed = discord.Embed(title="No worksheet ID specified!", description="Please specify a valid worksheet ID.", color=color("RED"))
embed.add_field(name="If want to see how to setup this bot use the command: ", value="```!setuphelp```", inline=False)
embed.set_thumbnail(url=picture("ERROR"))
await ctx.send(embed=embed)

"""
!export - Owner Only
This command exports all the roles and their permissions
from the Discord Server, organizes them and imports them
to the Google Sheet assigned to that Discord Server.
"""
@BOT.command()
@commands.has_permissions(administrator=True)
async def export(ctx):
file_name = str(ctx.guild.id) + ".txt"
try:
with open(path.join(path.dirname(path.realpath(__file__)) + r"\serverdata", file_name), "r+") as server_file:
spreadsheet_id = server_file.read()
try:
role_list = ctx.guild.roles # Export all the roles from a server. List of role type Objects.
role_list.reverse()
role_names = [role.name for role in role_list] # Get all the role names from the role Objects.
role_permissions = {role: dict(role.permissions) for role in role_list} # Put Roles in a dictionary and their permission_values in sub-dictionaries.
permission_names = list(role_permissions[role_list[0]].keys()) # Get all the permission names.
permission_values = permission_values_to_emojis(list(role_permissions.values()), permission_names) # Get all of the permissions values and convert them to √ or X.

clear_request = SERVICE.spreadsheets().values().clear(spreadsheetId=spreadsheet_id, range="A1:AH1000", body=clear_request_body())
titles_request = SERVICE.spreadsheets().values().batchUpdate(spreadsheetId=spreadsheet_id, body=titles_request_body(role_names, permission_names))
values_request = SERVICE.spreadsheets().values().batchUpdate(spreadsheetId=spreadsheet_id, body=values_request_body(permission_values))
clear_request.execute() # Clears the spreadsheet.
titles_request.execute()
values_request.execute() # Handling and execution of the requests to the Google API. See request_data.py for more info.

embed = discord.Embed(title="Permission Export Complete!", description="Your server's role permission_values have been successfully exported!", color=color("GREEN"))
embed.add_field(name="Here's the link to your worksheet: ", value=link("SPREADSHEET") + spreadsheet_id)
embed.set_thumbnail(url=picture("GSHEET"))
await ctx.send(embed=embed)
except Exception as exception:
print("Server ID:" + ctx.guild.id + "\n Exception:" + str(exception))
embed = discord.Embed(title="Worksheet unavailable!", description="There was an issue trying to access your server's worksheet!", color=color("RED"))
embed.add_field(name="Make sure you have followed the !setuphelp steps correctly. If the issue persists, contact the BOT Owner.", value="```!setuphelp```")
embed.set_thumbnail(url=picture("ERROR"))
await ctx.send(embed=embed)
except FileNotFoundError: # If the file does not exist, prompt user to configure.
embed = discord.Embed(title="No file found!", description="There was an issue trying to import your server's file from the database.", color=color("RED"))
embed.add_field(name="You have to configure your server first. Please try the command !setuphelp for more information.", value="```!setuphelp```")
embed.set_thumbnail(url=picture("ERROR"))
await ctx.send(embed=embed)

"""
BOT RUN Command that logs in the bot with our credentials.
Has to be in the end of the file.
"""
BOT.run('BOT_TOKEN_HERE')
1 change: 1 addition & 0 deletions Role_Manager_Bot/serverdata/SERVER_ID.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
SPREADSHEET_ID

0 comments on commit b0d7b57

Please sign in to comment.